From d7b74e6dcdee260e2e9e5b882f9009c9431f9fa5 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 15 Nov 2024 16:16:02 +0100 Subject: [PATCH 01/31] Fixes for the generation of entry code, fixes of hints parsing --- cmd/cli/main.go | 7 +- pkg/hintrunner/core/cairo_hintparser.go | 14 +++ pkg/hintrunner/core/hint.go | 6 +- pkg/hintrunner/core/hint_test.go | 6 +- pkg/parsers/starknet/hint.go | 10 +- pkg/runner/runner.go | 131 +++++++++++++----------- pkg/vm/builtins/builtin_runner.go | 2 + 7 files changed, 104 insertions(+), 72 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index dd9f6d6a4..3ae101ac1 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -203,6 +203,10 @@ func main() { if err != nil { return fmt.Errorf("cannot load program: %w", err) } + mainFunc, ok := cairoProgram.EntryPointsByFunction["main"] + if !ok { + return fmt.Errorf("cannot find main function") + } hints, err := core.GetCairoHints(cairoProgram) if err != nil { return fmt.Errorf("cannot get hints: %w", err) @@ -211,12 +215,13 @@ func main() { if err != nil { return fmt.Errorf("cannot load program: %w", err) } - entryCodeInstructions, err := runner.GetEntryCodeInstructions() + entryCodeInstructions, err := runner.GetEntryCodeInstructions(mainFunc, false, 0) if err != nil { return fmt.Errorf("cannot load entry code instructions: %w", err) } program.Bytecode = append(entryCodeInstructions, program.Bytecode...) program.Bytecode = append(program.Bytecode, runner.GetFooterInstructions()...) + fmt.Println(len(program.Bytecode)) runnerMode := runner.ExecutionMode if proofmode { runnerMode = runner.ProofModeCairo1 diff --git a/pkg/hintrunner/core/cairo_hintparser.go b/pkg/hintrunner/core/cairo_hintparser.go index 86168d73f..f0e25a131 100644 --- a/pkg/hintrunner/core/cairo_hintparser.go +++ b/pkg/hintrunner/core/cairo_hintparser.go @@ -142,6 +142,20 @@ func GetHintByName(hint starknet.Hint) (hinter.Hinter, error) { quotient: parseCellRefer(args.Quotient), remainder: parseCellRefer(args.Remainder), }, nil + case starknet.Uint256InvModNName: + args := hint.Args.(*starknet.Uint256InvModN) + return &Uint256InvModN{ + B0: parseResOperand(args.B0), + B1: parseResOperand(args.B1), + N0: parseResOperand(args.N0), + N1: parseResOperand(args.N1), + G0OrNoInv: parseCellRefer(args.G0OrNoInv), + G1Option: parseCellRefer(args.G1Option), + SOrR0: parseCellRefer(args.SOrR0), + SOrR1: parseCellRefer(args.SOrR1), + TOrK0: parseCellRefer(args.TOrK0), + TOrK1: parseCellRefer(args.TOrK1), + }, nil case starknet.Uint256DivModName: args := hint.Args.(*starknet.Uint256DivMod) return &Uint256DivMod{ diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 8e98ad233..3efa7a9fc 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -478,7 +478,7 @@ func (hint DivMod) Execute(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) e return nil } -type U256InvModN struct { +type Uint256InvModN struct { B0 hinter.Reference B1 hinter.Reference N0 hinter.Reference @@ -491,11 +491,11 @@ type U256InvModN struct { TOrK1 hinter.Reference } -func (hint U256InvModN) String() string { +func (hint Uint256InvModN) String() string { return "U256InvModN" } -func (hint U256InvModN) Execute(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { +func (hint Uint256InvModN) Execute(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { B0, err := hint.B0.Resolve(vm) if err != nil { return fmt.Errorf("resolve B0 operand %s: %v", hint.B0, err) diff --git a/pkg/hintrunner/core/hint_test.go b/pkg/hintrunner/core/hint_test.go index 39dbe0796..058c44eeb 100644 --- a/pkg/hintrunner/core/hint_test.go +++ b/pkg/hintrunner/core/hint_test.go @@ -1045,7 +1045,7 @@ func TestU256InvModN(t *testing.T) { N0Felt := f.NewElement(1) N1Felt := f.NewElement(0) - hint := U256InvModN{ + hint := Uint256InvModN{ B0: hinter.Immediate(B0Felt), B1: hinter.Immediate(B1Felt), N0: hinter.Immediate(N0Felt), @@ -1134,7 +1134,7 @@ func TestU256InvModN(t *testing.T) { N0Felt := f.NewElement(100) N1Felt := f.NewElement(0) - hint := U256InvModN{ + hint := Uint256InvModN{ B0: hinter.Immediate(B0Felt), B1: hinter.Immediate(B1Felt), N0: hinter.Immediate(N0Felt), @@ -1223,7 +1223,7 @@ func TestU256InvModN(t *testing.T) { N0Felt := f.NewElement(2) N1Felt := f.NewElement(0) - hint := U256InvModN{ + hint := Uint256InvModN{ B0: hinter.Immediate(B0Felt), B1: hinter.Immediate(B1Felt), N0: hinter.Immediate(N0Felt), diff --git a/pkg/parsers/starknet/hint.go b/pkg/parsers/starknet/hint.go index 01bc19ac8..5010f2650 100644 --- a/pkg/parsers/starknet/hint.go +++ b/pkg/parsers/starknet/hint.go @@ -24,7 +24,7 @@ const ( TestLessThanOrEqualAddressName HintName = "TestLessThanOrEqualAddress" WideMul128Name HintName = "WideMul128" DivModName HintName = "DivMod" - U256InvModName HintName = "U256InvMod" + Uint256InvModNName HintName = "U256InvModN" Uint256DivModName HintName = "Uint256DivMod" Uint512DivModByUint256Name HintName = "Uint512DivModByUint256" SquareRootName HintName = "SquareRoot" @@ -115,7 +115,7 @@ type DivMod struct { Remainder CellRef `json:"remainder" validate:"required"` } -type U256InvMod struct { +type Uint256InvModN struct { B0 ResOperand `json:"b0" validate:"required"` B1 ResOperand `json:"b1" validate:"required"` N0 ResOperand `json:"n0" validate:"required"` @@ -483,11 +483,9 @@ func (h *Hint) UnmarshalJSON(data []byte) error { if err != nil { return err } - for k, v := range rawHint { h.Name = HintName(k) var args any - switch h.Name { // Starknet hints case SystemCallName: @@ -509,8 +507,8 @@ func (h *Hint) UnmarshalJSON(data []byte) error { args = &WideMul128{} case DivModName: args = &DivMod{} - case U256InvModName: - args = &U256InvMod{} + case Uint256InvModNName: + args = &Uint256InvModN{} case Uint256DivModName: args = &Uint256DivMod{} case Uint512DivModByUint256Name: diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 66db1cba4..2956caa64 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -3,10 +3,12 @@ package runner import ( "errors" "fmt" + "slices" "github.com/NethermindEth/cairo-vm-go/pkg/assembler" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" + "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet" "github.com/NethermindEth/cairo-vm-go/pkg/utils" "github.com/NethermindEth/cairo-vm-go/pkg/vm" "github.com/NethermindEth/cairo-vm-go/pkg/vm/builtins" @@ -456,90 +458,101 @@ func (ctx *InlineCasmContext) AddInlineCASM(code string) { ctx.currentCodeOffset += int(total_size) } -func GetEntryCodeInstructions() ([]*fp.Element, error) { - //TODO: investigate how to implement function param types - paramTypes := []struct { - genericTypeId builtins.BuiltinType - size int - }{} +func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeForProof bool, initialGas uint64) ([]*fp.Element, error) { + paramTypes := function.InputArgs codeOffset := 0 + apOffset := 0 + builtinOffset := 3 + gotSegmentArena := false - ctx := &InlineCasmContext{} - - builtinOffset := map[builtins.BuiltinType]int{ - builtins.PedersenType: 10, - builtins.RangeCheckType: 9, - builtins.BitwiseType: 8, - builtins.ECOPType: 7, - builtins.PoseidonType: 6, - builtins.RangeCheck96Type: 5, - builtins.AddModeType: 4, - builtins.MulModType: 3, - } - + builtinsOffsetsMap := map[builtins.BuiltinType]int{} emulatedBuiltins := map[builtins.BuiltinType]struct{}{ - 1: {}, + builtins.SystemType: {}, + } + + for _, builtin := range []builtins.BuiltinType{ + builtins.MulModType, + builtins.AddModeType, + builtins.RangeCheck96Type, + builtins.PoseidonType, + builtins.ECOPType, + builtins.BitwiseType, + builtins.RangeCheckType, + builtins.PedersenType, + } { + if slices.Contains(function.Builtins, builtin) { + builtinsOffsetsMap[builtins.BuiltinType(builtin)] = builtinOffset + builtinOffset += 1 + } else { + gotSegmentArena = true + } } - apOffset := 0 + ctx := &InlineCasmContext{} paramsSize := 0 for _, param := range paramTypes { - ty, size := param.genericTypeId, param.size - if _, inBuiltin := builtinOffset[ty]; !inBuiltin { - if _, emulated := emulatedBuiltins[ty]; !emulated && ty != 99 { - paramsSize += size - } - } + paramsSize += param.Size + } + + hasPostCalculationLoop := gotSegmentArena && finalizeForProof + + localExprs := []struct{}{} + if hasPostCalculationLoop { + // TODO: Implement including local params } + ctx.AddInlineCASM( - fmt.Sprintf("ap += %d;", paramsSize), + fmt.Sprintf("ap += %d;", paramsSize+len(localExprs)), ) - apOffset += paramsSize - - for _, param := range paramTypes { - if param.genericTypeId == 99 { - ctx.AddInlineCASM( - `%{ memory[ap + 0] = segments.add() %} - %{ memory[ap + 1] = segments.add() %} - ap += 2; - [ap + 0] = 0, ap++; - [ap - 2] = [[ap - 3]]; - [ap - 1] = [[ap - 3] + 1]; - [ap - 1] = [[ap - 3] + 2]; - apOffset += 3`, - ) - } + apOffset += paramsSize + len(localExprs) + if gotSegmentArena { + // ctx.AddInlineCASM( + // ` + // %{ memory[ap + 0] = segments.add() %} + // %{ memory[ap + 1] = segments.add() %} + // ap += 2; + // [ap + 0] = 0, ap++; + // [ap - 2] = [[ap - 3]]; + // [ap - 1] = [[ap - 3] + 1]; + // [ap - 1] = [[ap - 3] + 2]; + // `, + // ) + // apOffset += 3 } usedArgs := 0 - for _, param := range paramTypes { - ty, tySize := param.genericTypeId, param.size - if offset, isBuiltin := builtinOffset[ty]; isBuiltin { + + for _, builtin := range function.Builtins { + if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( fmt.Sprintf("[ap + 0] = [fp - %d], ap++;", offset), ) apOffset += 1 - } else if _, emulated := emulatedBuiltins[ty]; emulated { + } else if _, emulated := emulatedBuiltins[builtin]; emulated { ctx.AddInlineCASM( - `memory[ap + 0] = segments.add(); - ap += 1;`, + ` + %{ memory[ap + 0] = segments.add() %} + ap += 1; + `, ) apOffset += 1 - } else if ty == 99 { + } else if builtin == builtins.SegmentArenaType { offset := apOffset - paramsSize ctx.AddInlineCASM( fmt.Sprintf("[ap + 0] = [ap - %d] + 3, ap++;", offset), ) apOffset += 1 - } else { - offset := apOffset - usedArgs - for i := 0; i < tySize; i++ { - ctx.AddInlineCASM( - fmt.Sprintf("[ap + 0] = [ap - %d], ap++;", offset), - ) - apOffset += 1 - usedArgs += 1 - } + } + } + + for _, param := range paramTypes { + offset := apOffset - usedArgs + for i := 0; i < param.Size; i++ { + ctx.AddInlineCASM( + fmt.Sprintf("[ap + 0] = [ap - %d], ap++;", offset), + ) + apOffset += 1 + usedArgs += 1 } } diff --git a/pkg/vm/builtins/builtin_runner.go b/pkg/vm/builtins/builtin_runner.go index 1b95fe77b..b20d6761f 100644 --- a/pkg/vm/builtins/builtin_runner.go +++ b/pkg/vm/builtins/builtin_runner.go @@ -24,6 +24,8 @@ const ( RangeCheck96Type AddModeType MulModType + GasBuiltinType + SystemType ) func Runner(name BuiltinType) memory.BuiltinRunner { From dfd9ec86b8bc1173b5a169da9e345939245ccfb1 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Thu, 21 Nov 2024 22:17:25 +0100 Subject: [PATCH 02/31] Add modifications to the runner --- cmd/cli/main.go | 1 - pkg/runner/runner.go | 34 +++++++++++----------------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 3ae101ac1..4254e3665 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -221,7 +221,6 @@ func main() { } program.Bytecode = append(entryCodeInstructions, program.Bytecode...) program.Bytecode = append(program.Bytecode, runner.GetFooterInstructions()...) - fmt.Println(len(program.Bytecode)) runnerMode := runner.ExecutionMode if proofmode { runnerMode = runner.ProofModeCairo1 diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 2956caa64..51d85a78a 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -460,7 +460,6 @@ func (ctx *InlineCasmContext) AddInlineCASM(code string) { func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeForProof bool, initialGas uint64) ([]*fp.Element, error) { paramTypes := function.InputArgs - codeOffset := 0 apOffset := 0 builtinOffset := 3 gotSegmentArena := false @@ -494,17 +493,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo paramsSize += param.Size } - hasPostCalculationLoop := gotSegmentArena && finalizeForProof - - localExprs := []struct{}{} - if hasPostCalculationLoop { - // TODO: Implement including local params - } - - ctx.AddInlineCASM( - fmt.Sprintf("ap += %d;", paramsSize+len(localExprs)), - ) - apOffset += paramsSize + len(localExprs) + apOffset += paramsSize if gotSegmentArena { // ctx.AddInlineCASM( // ` @@ -517,7 +506,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo // [ap - 1] = [[ap - 3] + 2]; // `, // ) - // apOffset += 3 + apOffset += 3 } usedArgs := 0 @@ -544,7 +533,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo apOffset += 1 } } - for _, param := range paramTypes { offset := apOffset - usedArgs for i := 0; i < param.Size; i++ { @@ -556,16 +544,16 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo } } - beforeFinalCall := ctx.currentCodeOffset - finalCallSize := 3 - offset := finalCallSize + codeOffset - ctx.AddInlineCASM(fmt.Sprintf(` - call rel %d; - ret; - `, offset)) - if beforeFinalCall+finalCallSize != ctx.currentCodeOffset { - return nil, errors.New("final call offset mismatch") + ctx.AddInlineCASM(fmt.Sprintf("call rel %d;", apOffset)) + for _, builtin := range function.Builtins { + if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { + ctx.AddInlineCASM( + fmt.Sprintf("[ap + 0] = [fp - %d], ap++;", offset), + ) + apOffset += 1 + } } + ctx.AddInlineCASM("ret;") return ctx.instructions, nil } From 83872d87f9d6b627491b07d68eb17ac7ff9b02e2 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Tue, 3 Dec 2024 01:14:39 +0100 Subject: [PATCH 03/31] Add fixes for the entrycode generation --- .gitignore | 1 + Makefile | 6 +-- ...fibonacci.cairo => fibonacci__small.cairo} | 0 integration_tests/cairo_vm_test.go | 8 +++- pkg/runner/runner.go | 42 +++++-------------- 5 files changed, 22 insertions(+), 35 deletions(-) rename integration_tests/cairo_1_programs/{fibonacci.cairo => fibonacci__small.cairo} (100%) diff --git a/.gitignore b/.gitignore index e69755e35..3e7d9c969 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ cov.out cpu.out *.test integration_tests/BenchMarks.txt +integration_tests/list_tests_in_progress.txt rust_vm_bin/ # Local Env Specific diff --git a/Makefile b/Makefile index c8f399358..79269ac45 100644 --- a/Makefile +++ b/Makefile @@ -58,12 +58,12 @@ integration: rm -rf ./cairo; \ 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; \ - cd ..; \ - mv cairo-vm/target/release/cairo1-run ../cairo/ && \ - mv cairo-vm/target/release/cairo-vm-cli ../../rust_vm_bin/ && \ + mv cairo-vm/target/release/cairo1-run ./cairo/ && \ + mv cairo-vm/target/release/cairo-vm-cli ../rust_vm_bin/ && \ rm -rf cairo-vm; \ cd ../../; \ fi; \ diff --git a/integration_tests/cairo_1_programs/fibonacci.cairo b/integration_tests/cairo_1_programs/fibonacci__small.cairo similarity index 100% rename from integration_tests/cairo_1_programs/fibonacci.cairo rename to integration_tests/cairo_1_programs/fibonacci__small.cairo diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index 4be973bd2..4c940cc1e 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -82,9 +82,11 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str if errorExpected { // we let the code go on so that we can check if the go vm also raises an error assert.Error(t, err, path) + return } else { if err != nil { t.Error(err) + writeToFile(path) return } } @@ -92,10 +94,12 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str elapsedGo, traceFile, memoryFile, _, err := runVm(compiledOutput, layout, zero) if errorExpected { assert.Error(t, err, path) + writeToFile(path) return } else { if err != nil { t.Error(err) + writeToFile(path) return } } @@ -103,11 +107,13 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str trace, memory, err := decodeProof(traceFile, memoryFile) if err != nil { t.Error(err) + writeToFile(path) return } rsTrace, rsMemory, err := decodeProof(rsTraceFile, rsMemoryFile) if err != nil { t.Error(err) + writeToFile(path) return } @@ -182,7 +188,7 @@ func TestCairoFiles(t *testing.T) { {"./cairo_zero_hint_tests/", true}, {"./cairo_zero_file_tests/", true}, {"./builtin_tests/", true}, - // {"./cairo_1_programs/", false}, + {"./cairo_1_programs/", false}, } // filter is for debugging purposes diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 59b62b8fa..7f44ce180 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -464,7 +464,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo paramTypes := function.InputArgs apOffset := 0 builtinOffset := 3 - gotSegmentArena := false builtinsOffsetsMap := map[builtins.BuiltinType]int{} emulatedBuiltins := map[builtins.BuiltinType]struct{}{ @@ -484,8 +483,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo if slices.Contains(function.Builtins, builtin) { builtinsOffsetsMap[builtins.BuiltinType(builtin)] = builtinOffset builtinOffset += 1 - } else { - gotSegmentArena = true } } @@ -494,23 +491,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo for _, param := range paramTypes { paramsSize += param.Size } - apOffset += paramsSize - if gotSegmentArena { - // ctx.AddInlineCASM( - // ` - // %{ memory[ap + 0] = segments.add() %} - // %{ memory[ap + 1] = segments.add() %} - // ap += 2; - // [ap + 0] = 0, ap++; - // [ap - 2] = [[ap - 3]]; - // [ap - 1] = [[ap - 3] + 1]; - // [ap - 1] = [[ap - 3] + 2]; - // `, - // ) - apOffset += 3 - } - usedArgs := 0 for _, builtin := range function.Builtins { @@ -533,28 +514,27 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo fmt.Sprintf("[ap + 0] = [ap - %d] + 3, ap++;", offset), ) apOffset += 1 - } - } - for _, param := range paramTypes { - offset := apOffset - usedArgs - for i := 0; i < param.Size; i++ { + } else if builtin == builtins.GasBuiltinType { ctx.AddInlineCASM( - fmt.Sprintf("[ap + 0] = [ap - %d], ap++;", offset), + ` + ap += 1; + `, ) apOffset += 1 usedArgs += 1 } } - - ctx.AddInlineCASM(fmt.Sprintf("call rel %d;", apOffset)) - for _, builtin := range function.Builtins { - if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { + for _, param := range paramTypes { + offset := apOffset - usedArgs + for i := 0; i < param.Size; i++ { ctx.AddInlineCASM( - fmt.Sprintf("[ap + 0] = [fp - %d], ap++;", offset), + fmt.Sprintf("[ap + 0] = [ap - %d], ap++;", offset), ) - apOffset += 1 + apOffset += param.Size + usedArgs += param.Size } } + ctx.AddInlineCASM(fmt.Sprintf("call rel %d;", apOffset+1)) ctx.AddInlineCASM("ret;") return ctx.instructions, nil From cd040994d4f038f47077c9d7b32ab1b03f7b6b70 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Tue, 10 Dec 2024 15:29:39 +0100 Subject: [PATCH 04/31] Refactor main CLI, offset the hints indexes by entry code size, load arguments and initial gas to the memory --- cmd/cli/main.go | 21 +++-------------- pkg/runner/runner.go | 56 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 4254e3665..082c3643e 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" - "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/core" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" hintrunner "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/zero" "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet" @@ -203,29 +202,15 @@ func main() { if err != nil { return fmt.Errorf("cannot load program: %w", err) } - mainFunc, ok := cairoProgram.EntryPointsByFunction["main"] - if !ok { - return fmt.Errorf("cannot find main function") - } - hints, err := core.GetCairoHints(cairoProgram) - if err != nil { - return fmt.Errorf("cannot get hints: %w", err) - } - program, err := runner.LoadCairoProgram(cairoProgram) + program, hints, err := runner.AssembleProgram(cairoProgram) if err != nil { - return fmt.Errorf("cannot load program: %w", err) + return fmt.Errorf("cannot assemble program: %w", err) } - entryCodeInstructions, err := runner.GetEntryCodeInstructions(mainFunc, false, 0) - if err != nil { - return fmt.Errorf("cannot load entry code instructions: %w", err) - } - program.Bytecode = append(entryCodeInstructions, program.Bytecode...) - program.Bytecode = append(program.Bytecode, runner.GetFooterInstructions()...) runnerMode := runner.ExecutionMode if proofmode { runnerMode = runner.ProofModeCairo1 } - 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) }, }, }, diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 7f44ce180..bac6daeec 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -7,6 +7,7 @@ import ( "github.com/NethermindEth/cairo-vm-go/pkg/assembler" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner" + "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/core" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet" "github.com/NethermindEth/cairo-vm-go/pkg/utils" @@ -57,6 +58,34 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru }, nil } +func AssembleProgram(cairoProgram *starknet.StarknetProgram) (Program, map[uint64][]hinter.Hinter, error) { + mainFunc, ok := cairoProgram.EntryPointsByFunction["main"] + if !ok { + return Program{}, nil, fmt.Errorf("cannot find main function") + } + program, err := LoadCairoProgram(cairoProgram) + if err != nil { + return Program{}, nil, fmt.Errorf("cannot load program: %w", err) + } + entryCodeInstructions, err := GetEntryCodeInstructions(mainFunc, false, 0) + 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 + } + 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 { @@ -86,7 +115,10 @@ func (runner *Runner) RunEntryPoint(pc uint64) error { if err != nil { return err } - + err = runner.loadArguments(uint64(0), uint64(8979879877)) + if err != nil { + return err + } if err := runner.RunUntilPc(&end); err != nil { return err } @@ -104,12 +136,16 @@ func (runner *Runner) Run() error { 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 } - if runner.runnerMode == ProofModeCairo0 || runner.runnerMode == ProofModeCairo1 { + if runner.isProofMode() { // +1 because proof mode require an extra instruction run // pow2 because proof mode also requires that the trace is a power of two pow2Steps := utils.NextPowerOfTwo(runner.vm.Step + 1) @@ -222,14 +258,18 @@ func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, stack := []mem.MemoryValue{} // adding to the stack only the builtins that are both in the program and in the layout for _, bRunner := range runner.layout.Builtins { - builtinSegment := memory.AllocateBuiltinSegment(bRunner.Runner) - if utils.Contains(runner.program.Builtins, bRunner.Builtin) { + if utils.Contains(runner.program.Builtins, bRunner.Builtin) || runner.isProofMode() { + builtinSegment := memory.AllocateBuiltinSegment(bRunner.Runner) stack = append(stack, mem.MemoryValueFromMemoryAddress(&builtinSegment)) } } return stack, nil } +func (runner *Runner) isProofMode() bool { + return runner.runnerMode == ProofModeCairo0 || runner.runnerMode == ProofModeCairo1 +} + func (runner *Runner) initializeVm( initialPC *mem.MemoryAddress, stack []mem.MemoryValue, memory *mem.Memory, cairo1FpOffset uint64, ) error { @@ -250,12 +290,18 @@ func (runner *Runner) initializeVm( Ap: initialFp, Fp: initialFp, }, memory, vm.VirtualMachineConfig{ - ProofMode: runner.runnerMode == ProofModeCairo0 || runner.runnerMode == ProofModeCairo1, + ProofMode: runner.isProofMode(), CollectTrace: runner.collectTrace, }) 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) { From 5eb27aba0ae2a742988919c5b563df8696317559 Mon Sep 17 00:00:00 2001 From: MaksymMalicki <81577596+MaksymMalicki@users.noreply.github.com> Date: Wed, 25 Dec 2024 15:55:01 -0500 Subject: [PATCH 05/31] Add available gas and user args (#677) * Add parsing logic for input user args * Add flags for available gas, input user args, writing args to memory * Fix unit tests for user arguments parsing * Lint the PR * Add user args to hint context * Refactor the code * Fix unconditional append of ExternalWriteArgsToMemory, bug fixes in integration tests * Add fixes of the call size calculation and include ExternalWriteArgsToMemory hint when gas present * Add layouts for integration tests * Add error handling * Fixes in entry code generation * Address changes mentioned in a discussion * Add comment regarding writing to memory in a hint for the future reference in the integration tests with args * Changes in calculations of the initial PC offset, CALL opcode offset incremented by mainFuncOffset, writing user args to the AP in the hint * Turn back VM config to private field * Add error handling on assign of `userArgs` to the initial scope * Lint project * Bump go version from 1.20 -> 1.21 (#678) * Bump go version from 1.20 -> 1.21 * Update golangci-lint * Simplify the Makefile * Correction in the makefile --- .github/workflows/lint.yml | 4 +- .github/workflows/unit-test.yml | 2 +- Makefile | 38 +++--- cmd/cli/main.go | 34 +++++- ...ct.cairo => dict_with_struct__small.cairo} | 0 ...naries.cairo => dictionaries__small.cairo} | 0 .../{sample.cairo => sample__small.cairo} | 0 integration_tests/cairo_vm_test.go | 8 +- pkg/hintrunner/core/hint.go | 35 ++++++ pkg/hintrunner/hintrunner.go | 12 +- pkg/hintrunner/hintrunner_test.go | 4 +- pkg/parsers/starknet/args.go | 56 +++++++++ pkg/parsers/starknet/args_test.go | 115 ++++++++++++++++++ pkg/runner/runner.go | 76 ++++++------ pkg/runner/runner_benchmark_test.go | 2 +- pkg/runner/runner_test.go | 8 +- pkg/vm/vm.go | 8 ++ 17 files changed, 326 insertions(+), 76 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%) rename integration_tests/cairo_1_programs/{sample.cairo => sample__small.cairo} (100%) create mode 100644 pkg/parsers/starknet/args.go create mode 100644 pkg/parsers/starknet/args_test.go diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 55839c55a..1f41bfa3b 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 1ef9abe77..a8e009f96 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 diff --git a/Makefile b/Makefile index 79269ac45..d3c129acb 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-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 ../../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/ && \ + 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-lang;\ + mv cairo-vm/target/release/cairo-vm-cli . ; \ rm -rf cairo-vm; \ - cd ../../; \ + cd ../; \ fi; \ go test ./integration_tests/... -v; \ else \ diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 082c3643e..151b4a285 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,9 +263,10 @@ 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) + runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName, userArgs) if err != nil { return fmt.Errorf("cannot create runner: %w", err) } 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 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 4c940cc1e..c38a14cf1 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -459,16 +459,18 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin } args := []string{ cliCommand, - "--proofmode", + // "--proofmode", "--tracefile", traceOutput, "--memoryfile", memoryOutput, "--layout", layout, - path, } - + if !zero { + args = append(args, "--available_gas", "9999999") + } + args = append(args, path) cmd := exec.Command( "../bin/cairo-vm", args..., diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 3efa7a9fc..202c6891f 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1929,3 +1929,38 @@ 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 { + 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) + err := vm.Memory.Write(1, vm.Context.Ap, &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/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index f8e54ebb0..c1a209e78 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,11 +15,18 @@ 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 { + context := *h.InitializeDefaultContext() + if userArgs != nil { + 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 // initialized only when required by the hint - context: *h.InitializeDefaultContext(), + context: context, hints: hints, } } diff --git a/pkg/hintrunner/hintrunner_test.go b/pkg/hintrunner/hintrunner_test.go index 0e057de98..c3cbbd5ed 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/parsers/starknet/args.go b/pkg/parsers/starknet/args.go new file mode 100644 index 000000000..0e7d28455 --- /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 000000000..f6bdd6f91 --- /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/runner/runner.go b/pkg/runner/runner.go index bac6daeec..f3228b17e 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 @@ -67,22 +67,25 @@ 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 } @@ -115,10 +118,6 @@ func (runner *Runner) RunEntryPoint(pc uint64) error { if err != nil { return err } - err = runner.loadArguments(uint64(0), uint64(8979879877)) - if err != nil { - return err - } if err := runner.RunUntilPc(&end); err != nil { return err } @@ -136,10 +135,6 @@ func (runner *Runner) Run() error { 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 @@ -181,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 { @@ -281,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 @@ -296,12 +298,6 @@ func (runner *Runner) initializeVm( 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) { @@ -506,11 +502,11 @@ 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 - + codeOffset := uint64(function.Offset) builtinsOffsetsMap := map[builtins.BuiltinType]int{} emulatedBuiltins := map[builtins.BuiltinType]struct{}{ builtins.SystemType: {}, @@ -539,7 +535,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo } apOffset += paramsSize usedArgs := 0 - + var hints map[uint64][]hinter.Hinter for _, builtin := range function.Builtins { if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( @@ -561,11 +557,12 @@ 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 } @@ -580,10 +577,13 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo usedArgs += param.Size } } - ctx.AddInlineCASM(fmt.Sprintf("call rel %d;", apOffset+1)) - ctx.AddInlineCASM("ret;") - - return ctx.instructions, nil + _, endInstructionsSize, err := assembler.CasmToBytecode("call rel 0; ret;") + if err != nil { + return nil, nil, err + } + totalSize := uint64(endInstructionsSize) + uint64(codeOffset) + ctx.AddInlineCASM(fmt.Sprintf("call rel %d; ret;", int(totalSize))) + 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 d86be979c..64bc7b879 100644 --- a/pkg/runner/runner_benchmark_test.go +++ b/pkg/runner/runner_benchmark_test.go @@ -233,7 +233,7 @@ 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) } diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 8c876d646..f94834cfa 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -27,7 +27,7 @@ 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() @@ -74,7 +74,7 @@ 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() @@ -133,7 +133,7 @@ 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() @@ -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 65c6ab8e4..7673f6ac5 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -91,6 +91,14 @@ type VirtualMachine struct { RcLimitsMax uint16 } +func (vm *VirtualMachine) PrintMemory() { + for i := range vm.Memory.Segments { + for j, cell := range vm.Memory.Segments[i].Data { + fmt.Printf("%d:%d %s\n", i, j, cell) + } + } +} + // NewVirtualMachine creates a VM from the program bytecode using a specified config. func NewVirtualMachine( initialContext Context, memory *mem.Memory, config VirtualMachineConfig, From 881204210a26001bacb0a2a8b89e2b1ee753b785 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Thu, 26 Dec 2024 20:38:21 +0100 Subject: [PATCH 06/31] Fix the integration tests --- cmd/cli/main.go | 8 +- integration_tests/cairo_vm_test.go | 131 +++++++++++++++------------- pkg/runner/runner.go | 34 +++++--- pkg/runner/runner_benchmark_test.go | 2 +- pkg/runner/runner_test.go | 8 +- 5 files changed, 102 insertions(+), 81 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 151b4a285..6a0fd39dd 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -121,9 +121,9 @@ func main() { if err != nil { return fmt.Errorf("cannot load program: %w", err) } - runnerMode := runner.ExecutionMode + runnerMode := runner.ExecutionModeZero if proofmode { - runnerMode = runner.ProofModeCairo0 + runnerMode = runner.ProofModeZero } return runVM(*program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode, nil) }, @@ -221,9 +221,9 @@ func main() { if err != nil { return fmt.Errorf("cannot assemble program: %w", err) } - runnerMode := runner.ExecutionMode + runnerMode := runner.ExecutionModeCairo if proofmode { - runnerMode = runner.ProofModeCairo1 + runnerMode = runner.ProofModeCairo } userArgs, err := starknet.ParseCairoProgramArgs(args) if err != nil { diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index c38a14cf1..09c8d28cd 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -74,14 +74,11 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str return } layout := getLayoutFromFileName(path) - rustVmFilePath := path - if zero { - rustVmFilePath = compiledOutput - } - elapsedRs, rsTraceFile, rsMemoryFile, err := runRustVm(rustVmFilePath, layout, zero) + + _, traceFile, memoryFile, _, err := runVm(compiledOutput, layout, zero) if errorExpected { - // we let the code go on so that we can check if the go vm also raises an error assert.Error(t, err, path) + writeToFile(path) return } else { if err != nil { @@ -91,10 +88,14 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str } } - elapsedGo, traceFile, memoryFile, _, err := runVm(compiledOutput, layout, zero) + rustVmFilePath := path + if zero { + rustVmFilePath = compiledOutput + } + _, rsTraceFile, rsMemoryFile, err := runRustVm(rustVmFilePath, layout, zero) if errorExpected { + // we let the code go on so that we can check if the go vm also raises an error assert.Error(t, err, path) - writeToFile(path) return } else { if err != nil { @@ -128,49 +129,49 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str writeToFile(path) } - if zero { - elapsedPy, pyTraceFile, pyMemoryFile, err := runPythonVm(compiledOutput, layout) - if errorExpected { - // we let the code go on so that we can check if the go vm also raises an error - assert.Error(t, err, path) - } else { - if err != nil { - t.Error(err) - return - } - } - - if benchmark { - benchmarkMap[name] = [3]int{int(elapsedPy.Milliseconds()), int(elapsedGo.Milliseconds()), int(elapsedRs.Milliseconds())} - } - - pyTrace, pyMemory, err := decodeProof(pyTraceFile, pyMemoryFile) - if err != nil { - t.Error(err) - return - } - - if !assert.Equal(t, pyTrace, rsTrace) { - t.Logf("pytrace:\n%s\n", traceRepr(pyTrace)) - t.Logf("rstrace:\n%s\n", traceRepr(rsTrace)) - writeToFile(path) - } - if !assert.Equal(t, pyMemory, rsMemory) { - t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory)) - t.Logf("rsmemory;\n%s\n", memoryRepr(rsMemory)) - writeToFile(path) - } - if !assert.Equal(t, pyTrace, trace) { - t.Logf("pytrace:\n%s\n", traceRepr(pyTrace)) - t.Logf("trace:\n%s\n", traceRepr(trace)) - writeToFile(path) - } - if !assert.Equal(t, pyMemory, memory) { - t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory)) - t.Logf("memory;\n%s\n", memoryRepr(memory)) - writeToFile(path) - } - } + // if zero { + // elapsedPy, pyTraceFile, pyMemoryFile, err := runPythonVm(compiledOutput, layout) + // if errorExpected { + // // we let the code go on so that we can check if the go vm also raises an error + // assert.Error(t, err, path) + // } else { + // if err != nil { + // t.Error(err) + // return + // } + // } + + // if benchmark { + // benchmarkMap[name] = [3]int{int(elapsedPy.Milliseconds()), int(elapsedGo.Milliseconds()), int(elapsedRs.Milliseconds())} + // } + + // pyTrace, pyMemory, err := decodeProof(pyTraceFile, pyMemoryFile) + // if err != nil { + // t.Error(err) + // return + // } + + // if !assert.Equal(t, pyTrace, rsTrace) { + // t.Logf("pytrace:\n%s\n", traceRepr(pyTrace)) + // t.Logf("rstrace:\n%s\n", traceRepr(rsTrace)) + // writeToFile(path) + // } + // if !assert.Equal(t, pyMemory, rsMemory) { + // t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory)) + // t.Logf("rsmemory;\n%s\n", memoryRepr(rsMemory)) + // writeToFile(path) + // } + // if !assert.Equal(t, pyTrace, trace) { + // t.Logf("pytrace:\n%s\n", traceRepr(pyTrace)) + // t.Logf("trace:\n%s\n", traceRepr(trace)) + // writeToFile(path) + // } + // if !assert.Equal(t, pyMemory, memory) { + // t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory)) + // t.Logf("memory;\n%s\n", memoryRepr(memory)) + // writeToFile(path) + // } + // } } var zerobench = flag.Bool("zerobench", false, "run integration tests and generate benchmarks file") @@ -241,9 +242,9 @@ func TestCairoFiles(t *testing.T) { wg.Wait() // wait for all goroutines to finish - for _, root := range roots { - clean(root.path) - } + // for _, root := range roots { + // clean(root.path) + // } if *zerobench { WriteBenchMarksToFile(benchmarkMap) @@ -335,7 +336,7 @@ func compileCairoCode(path string, zero bool) (string, error) { } } else { sierraOutput := swapExtenstion(path, sierraSuffix) - cliCommand = "../rust_vm_bin/cairo/cairo-compile" + cliCommand = "../rust_vm_bin/cairo-lang/cairo-compile" args = []string{ "--single-file", path, @@ -352,7 +353,7 @@ func compileCairoCode(path string, zero bool) (string, error) { ) } - cliCommand = "../rust_vm_bin/cairo/sierra-compile-json" + cliCommand = "../rust_vm_bin/cairo-lang/sierra-compile-json" args = []string{ sierraOutput, compiledOutput, @@ -426,7 +427,7 @@ func runRustVm(path, layout string, zero bool) (time.Duration, string, string, e args = append(args, "--proof_mode") } - binaryPath := "./../rust_vm_bin/cairo/cairo1-run" + binaryPath := "./../rust_vm_bin/cairo-lang/cairo1-run" if zero { binaryPath = "./../rust_vm_bin/cairo-vm-cli" } @@ -459,7 +460,7 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin } args := []string{ cliCommand, - // "--proofmode", + "--proofmode", "--tracefile", traceOutput, "--memoryfile", @@ -468,9 +469,21 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin layout, } if !zero { - args = append(args, "--available_gas", "9999999") + args = []string{ + cliCommand, + // "--proofmode", + "--tracefile", + traceOutput, + "--memoryfile", + memoryOutput, + "--layout", + layout, + "--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 f3228b17e..92306433c 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -20,9 +20,10 @@ import ( type RunnerMode uint8 const ( - ExecutionMode RunnerMode = iota + 1 - ProofModeCairo0 - ProofModeCairo1 + ExecutionModeZero RunnerMode = iota + 1 + ProofModeZero + ExecutionModeCairo + ProofModeCairo ) type Runner struct { @@ -111,7 +112,7 @@ func (runner *Runner) RunEntryPoint(pc uint64) error { returnFp := memory.AllocateEmptySegment() mvReturnFp := mem.MemoryValueFromMemoryAddress(&returnFp) cairo1FpOffset := uint64(0) - if runner.runnerMode == ProofModeCairo1 { + if runner.runnerMode == ProofModeCairo { cairo1FpOffset = 2 } end, err := runner.initializeEntrypoint(pc, nil, &mvReturnFp, memory, stack, cairo1FpOffset) @@ -173,17 +174,20 @@ func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { return mem.UnknownAddress, err } switch runner.runnerMode { - case ExecutionMode, ProofModeCairo1: + case ExecutionModeZero, ExecutionModeCairo, ProofModeCairo: returnFp := memory.AllocateEmptySegment() mvReturnFp := mem.MemoryValueFromMemoryAddress(&returnFp) - // 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) + if runner.isCairoMode() { + // In Cairo mainPCOffset is equal to the offset of program segment base + return runner.initializeEntrypoint(uint64(0), nil, &mvReturnFp, memory, stack, 2) } else { - return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 2) + mainPCOffset, ok := runner.program.Entrypoints["main"] + if !ok { + return mem.UnknownAddress, errors.New("can't find an entrypoint for main") + } + return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0) } - case ProofModeCairo0: + case ProofModeZero: initialPCOffset, ok := runner.program.Labels["__start__"] if !ok { return mem.UnknownAddress, @@ -260,7 +264,11 @@ func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, } func (runner *Runner) isProofMode() bool { - return runner.runnerMode == ProofModeCairo0 || runner.runnerMode == ProofModeCairo1 + return runner.runnerMode == ProofModeCairo || runner.runnerMode == ProofModeZero +} + +func (runner *Runner) isCairoMode() bool { + return runner.runnerMode == ExecutionModeCairo || runner.runnerMode == ProofModeCairo } func (runner *Runner) initializeVm( @@ -275,7 +283,7 @@ func (runner *Runner) initializeVm( } } // Write builtins costs segment address to the end of the program segment - if runner.runnerMode == ProofModeCairo1 || runner.runnerMode == ExecutionMode { + if runner.isCairoMode() { builtinsCostSegmentAddress := memory.AllocateEmptySegment() mv := mem.MemoryValueFromMemoryAddress(&builtinsCostSegmentAddress) programSegment := memory.Segments[vm.ProgramSegment] diff --git a/pkg/runner/runner_benchmark_test.go b/pkg/runner/runner_benchmark_test.go index 64bc7b879..6347640e6 100644 --- a/pkg/runner/runner_benchmark_test.go +++ b/pkg/runner/runner_benchmark_test.go @@ -233,7 +233,7 @@ func BenchmarkRunnerWithFibonacci(b *testing.B) { panic(err) } - runner, err := NewRunner(program, hints, ProofModeCairo0, false, math.MaxUint64, "plain", nil) + runner, err := NewRunner(program, hints, ProofModeZero, false, math.MaxUint64, "plain", nil) if err != nil { panic(err) } diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index f94834cfa..a20c074bd 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -27,7 +27,7 @@ func TestSimpleProgram(t *testing.T) { `) hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ExecutionMode, false, math.MaxUint64, "plain", nil) + runner, err := NewRunner(program, hints, ExecutionModeZero, false, math.MaxUint64, "plain", nil) require.NoError(t, err) endPc, err := runner.initializeMainEntrypoint() @@ -74,7 +74,7 @@ func TestStepLimitExceeded(t *testing.T) { `) hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ExecutionMode, false, 3, "plain", nil) + runner, err := NewRunner(program, hints, ExecutionModeZero, false, 3, "plain", nil) require.NoError(t, err) endPc, err := runner.initializeMainEntrypoint() @@ -133,7 +133,7 @@ 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", nil) + runner, err := NewRunner(program, hints, ProofModeZero, false, uint64(maxstep), "plain", nil) require.NoError(t, err) err = runner.Run() @@ -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, nil) + runner, err := NewRunner(program, hints, ProofModeZero, false, math.MaxUint64, layoutName, nil) if err != nil { panic(err) } From 78cf788365664c3d6f69632dd5eea71b0b27d9e6 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 27 Dec 2024 23:01:39 +0100 Subject: [PATCH 07/31] Fixes in the runner --- integration_tests/cairo_vm_test.go | 7 +++---- pkg/runner/runner.go | 28 ++++++++++++++++++---------- pkg/vm/vm.go | 3 +++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index 09c8d28cd..fdbcee594 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -242,9 +242,9 @@ func TestCairoFiles(t *testing.T) { wg.Wait() // wait for all goroutines to finish - // for _, root := range roots { - // clean(root.path) - // } + for _, root := range roots { + clean(root.path) + } if *zerobench { WriteBenchMarksToFile(benchmarkMap) @@ -483,7 +483,6 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin } } 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 92306433c..6c22bfdbf 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -242,21 +242,22 @@ func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, } // check if all builtins from the program are in the layout for _, programBuiltin := range runner.program.Builtins { - if programBuiltin != builtins.GasBuiltinType { - if _, found := builtinsSet[programBuiltin]; !found { - builtinName, err := programBuiltin.MarshalJSON() - if err != nil { - return []mem.MemoryValue{}, err - } - return []mem.MemoryValue{}, fmt.Errorf("builtin %s not found in the layout: %s", builtinName, runner.layout.Name) + if programBuiltin == builtins.GasBuiltinType || programBuiltin == builtins.SegmentArenaType { + continue + } + if _, found := builtinsSet[programBuiltin]; !found { + builtinName, err := programBuiltin.MarshalJSON() + if err != nil { + return []mem.MemoryValue{}, err } + return []mem.MemoryValue{}, fmt.Errorf("builtin %s not found in the layout: %s", builtinName, runner.layout.Name) } } stack := []mem.MemoryValue{} - // adding to the stack only the builtins that are both in the program and in the layout + for _, bRunner := range runner.layout.Builtins { - if utils.Contains(runner.program.Builtins, bRunner.Builtin) || runner.isProofMode() { - builtinSegment := memory.AllocateBuiltinSegment(bRunner.Runner) + builtinSegment := memory.AllocateBuiltinSegment(bRunner.Runner) + if utils.Contains(runner.program.Builtins, bRunner.Builtin) { stack = append(stack, mem.MemoryValueFromMemoryAddress(&builtinSegment)) } } @@ -309,6 +310,11 @@ func (runner *Runner) initializeVm( // run until the program counter equals the `pc` parameter func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { for !runner.vm.Context.Pc.Equal(pc) { + fmt.Println("pc", runner.pc(), "ap", runner.vm.Context.Ap, "fp", runner.vm.Context.Fp) + // if runner.steps() == 4 { + // runner.vm.PrintMemory() + // } + // runner.vm.PrintMemory() if runner.steps() >= runner.maxsteps { return fmt.Errorf( "pc %s step %d: max step limit exceeded (%d)", @@ -545,6 +551,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo usedArgs := 0 var hints map[uint64][]hinter.Hinter for _, builtin := range function.Builtins { + fmt.Println("builtin", builtin, builtin == builtins.GasBuiltinType) if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( fmt.Sprintf("[ap + 0] = [fp - %d], ap++;", offset), @@ -565,6 +572,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo ) apOffset += 1 } else if builtin == builtins.GasBuiltinType { + fmt.Println("builtin == builtins.GasBuiltinType") hints = map[uint64][]hinter.Hinter{ uint64(ctx.currentCodeOffset): { &core.ExternalWriteArgsToMemory{}, diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 7673f6ac5..743c4c7c9 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -94,6 +94,9 @@ type VirtualMachine struct { func (vm *VirtualMachine) PrintMemory() { for i := range vm.Memory.Segments { for j, cell := range vm.Memory.Segments[i].Data { + if !cell.Known() { + continue + } fmt.Printf("%d:%d %s\n", i, j, cell) } } From af47a3d4ee4d36956bf437374e6782fa42d341de Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Sat, 28 Dec 2024 13:50:49 +0100 Subject: [PATCH 08/31] Fixes in the runner --- pkg/runner/runner.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 6c22bfdbf..6e9e06ca4 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -177,9 +177,11 @@ func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { case ExecutionModeZero, ExecutionModeCairo, ProofModeCairo: returnFp := memory.AllocateEmptySegment() mvReturnFp := mem.MemoryValueFromMemoryAddress(&returnFp) - if runner.isCairoMode() { + if runner.runnerMode == ProofModeCairo { // In Cairo mainPCOffset is equal to the offset of program segment base return runner.initializeEntrypoint(uint64(0), nil, &mvReturnFp, memory, stack, 2) + } else if runner.runnerMode == ExecutionModeCairo { + return runner.initializeEntrypoint(uint64(0), nil, &mvReturnFp, memory, stack, 0) } else { mainPCOffset, ok := runner.program.Entrypoints["main"] if !ok { @@ -261,6 +263,16 @@ func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, stack = append(stack, mem.MemoryValueFromMemoryAddress(&builtinSegment)) } } + // Write builtins costs segment address to the end of the program segment + if runner.isCairoMode() { + builtinsCostSegmentAddress := memory.AllocateEmptySegment() + mv := mem.MemoryValueFromMemoryAddress(&builtinsCostSegmentAddress) + programSegment := memory.Segments[vm.ProgramSegment] + err := memory.Write(0, programSegment.Len(), &mv) + if err != nil { + return nil, err + } + } return stack, nil } @@ -283,16 +295,7 @@ func (runner *Runner) initializeVm( return err } } - // Write builtins costs segment address to the end of the program segment - if runner.isCairoMode() { - builtinsCostSegmentAddress := memory.AllocateEmptySegment() - mv := mem.MemoryValueFromMemoryAddress(&builtinsCostSegmentAddress) - programSegment := memory.Segments[vm.ProgramSegment] - err := memory.Write(0, programSegment.Len(), &mv) - if err != nil { - return err - } - } + fmt.Println("offset", offset, "stackSize", stackSize, "cairo1FpOffset", cairo1FpOffset) initialFp := offset + uint64(len(stack)) + cairo1FpOffset var err error // initialize vm From 73f6dd5beb7d1c8633c0d78bd508eff4f38a87a7 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Sat, 28 Dec 2024 14:52:09 +0100 Subject: [PATCH 09/31] Fix the unit tests, uncomment pythonVm execution in integration tests, code cleanups --- integration_tests/cairo_vm_test.go | 80 +++++++++++++----------------- pkg/runner/runner.go | 9 +--- pkg/runner/runner_test.go | 3 +- 3 files changed, 37 insertions(+), 55 deletions(-) diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index fdbcee594..4b6a030b2 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -75,7 +75,7 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str } layout := getLayoutFromFileName(path) - _, traceFile, memoryFile, _, err := runVm(compiledOutput, layout, zero) + elapsedGo, traceFile, memoryFile, _, err := runVm(compiledOutput, layout, zero) if errorExpected { assert.Error(t, err, path) writeToFile(path) @@ -92,7 +92,7 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str if zero { rustVmFilePath = compiledOutput } - _, rsTraceFile, rsMemoryFile, err := runRustVm(rustVmFilePath, layout, zero) + elapsedRs, rsTraceFile, rsMemoryFile, err := runRustVm(rustVmFilePath, layout, zero) if errorExpected { // we let the code go on so that we can check if the go vm also raises an error assert.Error(t, err, path) @@ -129,49 +129,39 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str writeToFile(path) } - // if zero { - // elapsedPy, pyTraceFile, pyMemoryFile, err := runPythonVm(compiledOutput, layout) - // if errorExpected { - // // we let the code go on so that we can check if the go vm also raises an error - // assert.Error(t, err, path) - // } else { - // if err != nil { - // t.Error(err) - // return - // } - // } - - // if benchmark { - // benchmarkMap[name] = [3]int{int(elapsedPy.Milliseconds()), int(elapsedGo.Milliseconds()), int(elapsedRs.Milliseconds())} - // } - - // pyTrace, pyMemory, err := decodeProof(pyTraceFile, pyMemoryFile) - // if err != nil { - // t.Error(err) - // return - // } - - // if !assert.Equal(t, pyTrace, rsTrace) { - // t.Logf("pytrace:\n%s\n", traceRepr(pyTrace)) - // t.Logf("rstrace:\n%s\n", traceRepr(rsTrace)) - // writeToFile(path) - // } - // if !assert.Equal(t, pyMemory, rsMemory) { - // t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory)) - // t.Logf("rsmemory;\n%s\n", memoryRepr(rsMemory)) - // writeToFile(path) - // } - // if !assert.Equal(t, pyTrace, trace) { - // t.Logf("pytrace:\n%s\n", traceRepr(pyTrace)) - // t.Logf("trace:\n%s\n", traceRepr(trace)) - // writeToFile(path) - // } - // if !assert.Equal(t, pyMemory, memory) { - // t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory)) - // t.Logf("memory;\n%s\n", memoryRepr(memory)) - // writeToFile(path) - // } - // } + if zero { + elapsedPy, pyTraceFile, pyMemoryFile, err := runPythonVm(compiledOutput, layout) + if errorExpected { + // we let the code go on so that we can check if the go vm also raises an error + assert.Error(t, err, path) + } else { + if err != nil { + t.Error(err) + return + } + } + + if benchmark { + benchmarkMap[name] = [3]int{int(elapsedPy.Milliseconds()), int(elapsedGo.Milliseconds()), int(elapsedRs.Milliseconds())} + } + + pyTrace, pyMemory, err := decodeProof(pyTraceFile, pyMemoryFile) + if err != nil { + t.Error(err) + return + } + + if !assert.Equal(t, pyTrace, trace) { + t.Logf("pytrace:\n%s\n", traceRepr(pyTrace)) + t.Logf("trace:\n%s\n", traceRepr(trace)) + writeToFile(path) + } + if !assert.Equal(t, pyMemory, memory) { + t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory)) + t.Logf("memory;\n%s\n", memoryRepr(memory)) + writeToFile(path) + } + } } var zerobench = flag.Bool("zerobench", false, "run integration tests and generate benchmarks file") diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 6e9e06ca4..057c97a10 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -190,6 +190,7 @@ func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0) } case ProofModeZero: + fmt.Println("program: ", runner.program.Labels) initialPCOffset, ok := runner.program.Labels["__start__"] if !ok { return mem.UnknownAddress, @@ -295,7 +296,6 @@ func (runner *Runner) initializeVm( return err } } - fmt.Println("offset", offset, "stackSize", stackSize, "cairo1FpOffset", cairo1FpOffset) initialFp := offset + uint64(len(stack)) + cairo1FpOffset var err error // initialize vm @@ -313,11 +313,6 @@ func (runner *Runner) initializeVm( // run until the program counter equals the `pc` parameter func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { for !runner.vm.Context.Pc.Equal(pc) { - fmt.Println("pc", runner.pc(), "ap", runner.vm.Context.Ap, "fp", runner.vm.Context.Fp) - // if runner.steps() == 4 { - // runner.vm.PrintMemory() - // } - // runner.vm.PrintMemory() if runner.steps() >= runner.maxsteps { return fmt.Errorf( "pc %s step %d: max step limit exceeded (%d)", @@ -554,7 +549,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo usedArgs := 0 var hints map[uint64][]hinter.Hinter for _, builtin := range function.Builtins { - fmt.Println("builtin", builtin, builtin == builtins.GasBuiltinType) if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( fmt.Sprintf("[ap + 0] = [fp - %d], ap++;", offset), @@ -575,7 +569,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo ) apOffset += 1 } else if builtin == builtins.GasBuiltinType { - fmt.Println("builtin == builtins.GasBuiltinType") hints = map[uint64][]hinter.Hinter{ uint64(ctx.currentCodeOffset): { &core.ExternalWriteArgsToMemory{}, diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index a20c074bd..50ded3f4d 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -434,9 +434,8 @@ func TestModuloBuiltin(t *testing.T) { func createRunner(code string, layoutName string, builtins ...builtins.BuiltinType) Runner { program := createProgramWithBuiltins(code, builtins...) - hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ProofModeZero, false, math.MaxUint64, layoutName, nil) + runner, err := NewRunner(program, hints, ExecutionModeZero, false, math.MaxUint64, layoutName, nil) if err != nil { panic(err) } From 17b17df72d21c36ae082ad093a3e71c77293f0c7 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Tue, 7 Jan 2025 11:35:22 +0100 Subject: [PATCH 10/31] Add writing tokens gas cost to memory --- integration_tests/.env | 2 +- pkg/runner/gas.go | 72 ++++++++++++++++++++++++++++++++++++++++++ pkg/runner/runner.go | 5 +-- 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 pkg/runner/gas.go diff --git a/integration_tests/.env b/integration_tests/.env index 5448ff22e..e72fd423a 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=ecdsa_recover__starknet.cairo \ No newline at end of file diff --git a/pkg/runner/gas.go b/pkg/runner/gas.go new file mode 100644 index 000000000..86c89c395 --- /dev/null +++ b/pkg/runner/gas.go @@ -0,0 +1,72 @@ +package runner + +import ( + "fmt" + + "github.com/NethermindEth/cairo-vm-go/pkg/vm" + "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" + mem "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" +) + +type TokenGasCost uint8 + +const ( + ConstToken TokenGasCost = iota + 1 + HoleToken + RangeCheckToken + RangeCheck96Token + PedersenToken + PoseidonToken + BitwiseToken + EcOpToken + AddModToken + MulModToken +) + +func getTokenGasCost(token TokenGasCost) (uint64, error) { + switch token { + case ConstToken: + return 1, nil + case PedersenToken: + return 4050, nil + case PoseidonToken: + return 491, nil + case BitwiseToken: + return 583, nil + case EcOpToken: + return 4085, nil + case AddModToken: + return 230, nil + case MulModToken: + return 604, nil + default: + return 0, fmt.Errorf("token has no cost") + } +} + +func gasInitialization(memory *memory.Memory) error { + builtinsCostSegmentAddress := memory.AllocateEmptySegment() + mv := mem.MemoryValueFromMemoryAddress(&builtinsCostSegmentAddress) + programSegment := memory.Segments[vm.ProgramSegment] + err := memory.Write(0, programSegment.Len(), &mv) + if err != nil { + return err + } + preCostTokenTypes := []TokenGasCost{PedersenToken, PoseidonToken, BitwiseToken, EcOpToken, AddModToken, MulModToken} + for _, token := range preCostTokenTypes { + cost, err := getTokenGasCost(token) + if err != nil { + return err + } + mv := mem.MemoryValueFromUint(cost) + err = memory.WriteToAddress(&builtinsCostSegmentAddress, &mv) + if err != nil { + return err + } + builtinsCostSegmentAddress, err = builtinsCostSegmentAddress.AddOffset(1) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 057c97a10..f1572a98b 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -266,10 +266,7 @@ func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, } // Write builtins costs segment address to the end of the program segment if runner.isCairoMode() { - builtinsCostSegmentAddress := memory.AllocateEmptySegment() - mv := mem.MemoryValueFromMemoryAddress(&builtinsCostSegmentAddress) - programSegment := memory.Segments[vm.ProgramSegment] - err := memory.Write(0, programSegment.Len(), &mv) + err := gasInitialization(memory) if err != nil { return nil, err } From 688b3d2e92cee6a43e1286100751608087661126 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 8 Jan 2025 02:00:42 +0100 Subject: [PATCH 11/31] Proper builtins initialization for cairo mode --- integration_tests/.env | 2 +- pkg/runner/runner.go | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/integration_tests/.env b/integration_tests/.env index e72fd423a..5448ff22e 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=ecdsa_recover__starknet.cairo \ No newline at end of file +INTEGRATION_TESTS_FILTERS= \ No newline at end of file diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index f1572a98b..2b3cc36da 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -190,7 +190,6 @@ func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0) } case ProofModeZero: - fmt.Println("program: ", runner.program.Labels) initialPCOffset, ok := runner.program.Labels["__start__"] if !ok { return mem.UnknownAddress, @@ -259,9 +258,16 @@ func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, stack := []mem.MemoryValue{} for _, bRunner := range runner.layout.Builtins { - builtinSegment := memory.AllocateBuiltinSegment(bRunner.Runner) - if utils.Contains(runner.program.Builtins, bRunner.Builtin) { - stack = append(stack, mem.MemoryValueFromMemoryAddress(&builtinSegment)) + if runner.isCairoMode() { + if utils.Contains(runner.program.Builtins, bRunner.Builtin) { + builtinSegment := memory.AllocateBuiltinSegment(bRunner.Runner) + stack = append(stack, mem.MemoryValueFromMemoryAddress(&builtinSegment)) + } + } else { + builtinSegment := memory.AllocateBuiltinSegment(bRunner.Runner) + if utils.Contains(runner.program.Builtins, bRunner.Builtin) { + stack = append(stack, mem.MemoryValueFromMemoryAddress(&builtinSegment)) + } } } // Write builtins costs segment address to the end of the program segment From f105506549f95f3415de4ff40c9451da87237559 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Thu, 9 Jan 2025 13:33:55 +0100 Subject: [PATCH 12/31] Address comments in the PR --- integration_tests/cairo_vm_test.go | 2 +- pkg/hintrunner/hintrunner.go | 1 + pkg/runner/gas.go | 8 +++----- pkg/runner/runner.go | 11 ----------- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index 4b6a030b2..1ae4e157f 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -179,7 +179,7 @@ func TestCairoFiles(t *testing.T) { {"./cairo_zero_hint_tests/", true}, {"./cairo_zero_file_tests/", true}, {"./builtin_tests/", true}, - {"./cairo_1_programs/", false}, + // {"./cairo_1_programs/", false}, } // filter is for debugging purposes diff --git a/pkg/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index c1a209e78..92bb64487 100644 --- a/pkg/hintrunner/hintrunner.go +++ b/pkg/hintrunner/hintrunner.go @@ -19,6 +19,7 @@ func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArg context := *h.InitializeDefaultContext() if userArgs != nil { err := context.ScopeManager.AssignVariable("userArgs", userArgs) + // Error handling: this condition should never be true, since the context was initialized above if err != nil { panic(fmt.Errorf("assign userArgs: %v", err)) } diff --git a/pkg/runner/gas.go b/pkg/runner/gas.go index 86c89c395..b8c9ab724 100644 --- a/pkg/runner/gas.go +++ b/pkg/runner/gas.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/NethermindEth/cairo-vm-go/pkg/vm" - "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" mem "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" ) @@ -12,9 +11,6 @@ type TokenGasCost uint8 const ( ConstToken TokenGasCost = iota + 1 - HoleToken - RangeCheckToken - RangeCheck96Token PedersenToken PoseidonToken BitwiseToken @@ -23,6 +19,8 @@ const ( MulModToken ) +// Approximated costs token types +// Src: https://github.com/starkware-libs/cairo/blob/9ac17df38f28f267e03a6522d12031976a66d305/crates/cairo-lang-runner/src/lib.rs#L109 func getTokenGasCost(token TokenGasCost) (uint64, error) { switch token { case ConstToken: @@ -44,7 +42,7 @@ func getTokenGasCost(token TokenGasCost) (uint64, error) { } } -func gasInitialization(memory *memory.Memory) error { +func gasInitialization(memory *mem.Memory) error { builtinsCostSegmentAddress := memory.AllocateEmptySegment() mv := mem.MemoryValueFromMemoryAddress(&builtinsCostSegmentAddress) programSegment := memory.Segments[vm.ProgramSegment] diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 2b3cc36da..b5cfa851b 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -523,9 +523,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo builtinOffset := 3 codeOffset := uint64(function.Offset) builtinsOffsetsMap := map[builtins.BuiltinType]int{} - emulatedBuiltins := map[builtins.BuiltinType]struct{}{ - builtins.SystemType: {}, - } for _, builtin := range []builtins.BuiltinType{ builtins.MulModType, @@ -557,14 +554,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo fmt.Sprintf("[ap + 0] = [fp - %d], ap++;", offset), ) apOffset += 1 - } else if _, emulated := emulatedBuiltins[builtin]; emulated { - ctx.AddInlineCASM( - ` - %{ memory[ap + 0] = segments.add() %} - ap += 1; - `, - ) - apOffset += 1 } else if builtin == builtins.SegmentArenaType { offset := apOffset - paramsSize ctx.AddInlineCASM( From cc8293808176da41aca1447a374f5df07c8d7a76 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 10 Jan 2025 15:35:35 +0100 Subject: [PATCH 13/31] Fix bugs regarding dicts --- pkg/hintrunner/core/hint.go | 22 ++++++++++++++----- pkg/runner/runner.go | 44 ++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 202c6891f..e5e57f12f 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1153,7 +1153,8 @@ func (hint *Felt252DictEntryInit) Execute(vm *VM.VirtualMachine, ctx *hinter.Hin prevValue, err := ctx.DictionaryManager.At(dictPtr, key) if err != nil { - return fmt.Errorf("get dictionary entry: %w", err) + mv := mem.MemoryValueFromFieldElement(&utils.FeltZero) + prevValue = &mv } if prevValue == nil { mv := mem.EmptyMemoryValueAsFelt() @@ -1245,6 +1246,7 @@ func (hint *InitSquashData) String() string { func (hint *InitSquashData) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { // todo(rodro): Don't know if it could be called multiple times, or + fmt.Println(ctx.SquashedDictionaryManager.Keys, ctx.SquashedDictionaryManager.KeyToIndices) err := hinter.InitializeSquashedDictionaryManager(ctx) if err != nil { return err @@ -1282,7 +1284,7 @@ func (hint *InitSquashData) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunne // sort the keys in descending order sort.Slice(ctx.SquashedDictionaryManager.Keys, func(i, j int) bool { - return ctx.SquashedDictionaryManager.Keys[i].Cmp(&ctx.SquashedDictionaryManager.Keys[j]) < 0 + return ctx.SquashedDictionaryManager.Keys[i].Cmp(&ctx.SquashedDictionaryManager.Keys[j]) > 0 }) // if the first key is bigger than 2^128, signal it @@ -1411,11 +1413,15 @@ func (hint *ShouldContinueSquashLoop) Execute(vm *VM.VirtualMachine, ctx *hinter } var shouldContinueLoop f.Element - if lastIndices, err := ctx.SquashedDictionaryManager.LastIndices(); err == nil && len(lastIndices) <= 1 { - shouldContinueLoop.SetOne() - } else if err != nil { + lastIndices, err := ctx.SquashedDictionaryManager.LastIndices() + if err != nil { return fmt.Errorf("get last indices: %w", err) } + if len(lastIndices) > 1 { + shouldContinueLoop.SetOne() + } else { + shouldContinueLoop.SetZero() + } mv := mem.MemoryValueFromFieldElement(&shouldContinueLoop) return vm.Memory.WriteToAddress(&shouldContinuePtr, &mv) @@ -1435,11 +1441,15 @@ func (hint *GetNextDictKey) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunne return fmt.Errorf("get next key address: %w", err) } - nextKey, err := ctx.SquashedDictionaryManager.PopKey() + _, err = ctx.SquashedDictionaryManager.PopKey() if err != nil { return fmt.Errorf("pop key: %w", err) } + nextKey, err := ctx.SquashedDictionaryManager.LastKey() + if err != nil { + return fmt.Errorf("get last key: %w", err) + } mv := mem.MemoryValueFromFieldElement(&nextKey) return vm.Memory.WriteToAddress(&nextKeyAddr, &mv) } diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index b5cfa851b..d82244af9 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -316,6 +316,9 @@ func (runner *Runner) initializeVm( // run until the program counter equals the `pc` parameter func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { for !runner.vm.Context.Pc.Equal(pc) { + if runner.vm.Context.Pc.Offset == 166 { + runner.vm.PrintMemory() + } if runner.steps() >= runner.maxsteps { return fmt.Errorf( "pc %s step %d: max step limit exceeded (%d)", @@ -541,13 +544,46 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo } ctx := &InlineCasmContext{} + + gotSegmentArena := false + for _, builtin := range function.Builtins { + if builtin == builtins.SegmentArenaType { + gotSegmentArena = true + } + } + + hints := make(map[uint64][]hinter.Hinter) + + if gotSegmentArena { + hints[uint64(ctx.currentCodeOffset)] = []hinter.Hinter{ + &core.AllocSegment{ + Dst: hinter.ApCellRef(0), + }, + &core.AllocSegment{ + Dst: hinter.ApCellRef(1), + }, + } + ctx.AddInlineCASM( + "[ap+2] = 0, ap++;", + ) + ctx.AddInlineCASM( + "[ap] = [[ap-1]], ap++;", + ) + ctx.AddInlineCASM( + ` + [ap] = [[ap-2]+1], ap++; + [ap-1] = [[ap-3]+2]; + `, + ) + apOffset += 3 + } + paramsSize := 0 for _, param := range paramTypes { paramsSize += param.Size } apOffset += paramsSize usedArgs := 0 - var hints map[uint64][]hinter.Hinter for _, builtin := range function.Builtins { if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( @@ -561,10 +597,8 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo ) apOffset += 1 } else if builtin == builtins.GasBuiltinType { - hints = map[uint64][]hinter.Hinter{ - uint64(ctx.currentCodeOffset): { - &core.ExternalWriteArgsToMemory{}, - }, + hints[uint64(ctx.currentCodeOffset)] = []hinter.Hinter{ + &core.ExternalWriteArgsToMemory{}, } ctx.AddInlineCASM("ap += 1;") apOffset += 1 From a32add7a756c16fbd13febd0ff6dcb2b44ecdd56 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 10 Jan 2025 15:36:14 +0100 Subject: [PATCH 14/31] Remove prints --- pkg/hintrunner/core/hint.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index e5e57f12f..f6e348d79 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1246,7 +1246,6 @@ func (hint *InitSquashData) String() string { func (hint *InitSquashData) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { // todo(rodro): Don't know if it could be called multiple times, or - fmt.Println(ctx.SquashedDictionaryManager.Keys, ctx.SquashedDictionaryManager.KeyToIndices) err := hinter.InitializeSquashedDictionaryManager(ctx) if err != nil { return err From 53d41909a76f0e1998c3c61aa11c5c529c08fba1 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Sat, 11 Jan 2025 16:08:29 +0100 Subject: [PATCH 15/31] Fixes of the last tests for the dicts --- pkg/hintrunner/core/hint.go | 3 ++- pkg/runner/runner.go | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index b87c6fd77..dbc26392f 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1238,7 +1238,8 @@ func (hint *InitSquashData) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunne // todo(rodro): Don't know if it could be called multiple times, or err := hinter.InitializeSquashedDictionaryManager(ctx) if err != nil { - return err + ctx.SquashedDictionaryManager = hinter.SquashedDictionaryManager{} + _ = hinter.InitializeSquashedDictionaryManager(ctx) } dictAccessPtr, err := hinter.ResolveAsAddress(vm, hint.DictAccesses) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 80ee11bbd..225f92666 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -316,9 +316,6 @@ func (runner *Runner) initializeVm( // run until the program counter equals the `pc` parameter func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { for !runner.vm.Context.Pc.Equal(pc) { - if runner.vm.Context.Pc.Offset == 166 { - runner.vm.PrintMemory() - } if runner.steps() >= runner.maxsteps { return fmt.Errorf( "pc %s step %d: max step limit exceeded (%d)", From 31aeeedacc82499b9c3482a0543886f328eb2f94 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Sat, 11 Jan 2025 16:15:25 +0100 Subject: [PATCH 16/31] Add dict_non_squashed dir to the integration tests --- integration_tests/cairo_vm_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index 1ae4e157f..d68220650 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -180,6 +180,7 @@ func TestCairoFiles(t *testing.T) { {"./cairo_zero_file_tests/", true}, {"./builtin_tests/", true}, // {"./cairo_1_programs/", false}, + // {"./cairo_1_programs/dict_non_squashed", false}, } // filter is for debugging purposes From 086011adaec2c913d62c5148a3b34eaa395ede0d Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Sat, 11 Jan 2025 22:20:52 +0100 Subject: [PATCH 17/31] Add checks for the matching args size, rename files, modify the integration tests pipeline --- cmd/cli/main.go | 10 ++-- ...sum.cairo => array_input_sum__small.cairo} | 0 ...length.cairo => array_length__small.cairo} | 0 ...put.cairo => dict_with_input__small.cairo} | 0 .../{tensor.cairo => tensor__small.cairo} | 0 integration_tests/cairo_vm_test.go | 48 ++++++++++++------- pkg/runner/runner.go | 21 ++++++-- 7 files changed, 52 insertions(+), 27 deletions(-) rename integration_tests/cairo_1_programs/with_input/{array_input_sum.cairo => array_input_sum__small.cairo} (100%) rename integration_tests/cairo_1_programs/with_input/{array_length.cairo => array_length__small.cairo} (100%) rename integration_tests/cairo_1_programs/with_input/{dict_with_input.cairo => dict_with_input__small.cairo} (100%) rename integration_tests/cairo_1_programs/with_input/{tensor.cairo => tensor__small.cairo} (100%) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 6a0fd39dd..5a2c858ca 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -217,7 +217,11 @@ func main() { if err != nil { return fmt.Errorf("cannot load program: %w", err) } - program, hints, err := runner.AssembleProgram(cairoProgram) + userArgs, err := starknet.ParseCairoProgramArgs(args) + if err != nil { + return fmt.Errorf("cannot parse args: %w", err) + } + program, hints, err := runner.AssembleProgram(cairoProgram, userArgs) if err != nil { return fmt.Errorf("cannot assemble program: %w", err) } @@ -225,10 +229,6 @@ func main() { if proofmode { runnerMode = runner.ProofModeCairo } - 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{ diff --git a/integration_tests/cairo_1_programs/with_input/array_input_sum.cairo b/integration_tests/cairo_1_programs/with_input/array_input_sum__small.cairo similarity index 100% rename from integration_tests/cairo_1_programs/with_input/array_input_sum.cairo rename to integration_tests/cairo_1_programs/with_input/array_input_sum__small.cairo diff --git a/integration_tests/cairo_1_programs/with_input/array_length.cairo b/integration_tests/cairo_1_programs/with_input/array_length__small.cairo similarity index 100% rename from integration_tests/cairo_1_programs/with_input/array_length.cairo rename to integration_tests/cairo_1_programs/with_input/array_length__small.cairo diff --git a/integration_tests/cairo_1_programs/with_input/dict_with_input.cairo b/integration_tests/cairo_1_programs/with_input/dict_with_input__small.cairo similarity index 100% rename from integration_tests/cairo_1_programs/with_input/dict_with_input.cairo rename to integration_tests/cairo_1_programs/with_input/dict_with_input__small.cairo diff --git a/integration_tests/cairo_1_programs/with_input/tensor.cairo b/integration_tests/cairo_1_programs/with_input/tensor__small.cairo similarity index 100% rename from integration_tests/cairo_1_programs/with_input/tensor.cairo rename to integration_tests/cairo_1_programs/with_input/tensor__small.cairo diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index d68220650..65e6bfd63 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -66,7 +66,7 @@ func (f *Filter) filtered(testFile string) bool { return false } -func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[string][3]int, benchmark bool, errorExpected bool, zero bool) { +func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[string][3]int, benchmark bool, errorExpected bool, zero bool, inputArgs string) { t.Logf("testing: %s\n", path) compiledOutput, err := compileCairoCode(path, zero) if err != nil { @@ -75,7 +75,7 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str } layout := getLayoutFromFileName(path) - elapsedGo, traceFile, memoryFile, _, err := runVm(compiledOutput, layout, zero) + elapsedGo, traceFile, memoryFile, _, err := runVm(compiledOutput, layout, zero, inputArgs) if errorExpected { assert.Error(t, err, path) writeToFile(path) @@ -92,7 +92,7 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str if zero { rustVmFilePath = compiledOutput } - elapsedRs, rsTraceFile, rsMemoryFile, err := runRustVm(rustVmFilePath, layout, zero) + elapsedRs, rsTraceFile, rsMemoryFile, err := runRustVm(rustVmFilePath, layout, zero, inputArgs) if errorExpected { // we let the code go on so that we can check if the go vm also raises an error assert.Error(t, err, path) @@ -171,16 +171,27 @@ func TestCairoFiles(t *testing.T) { if err != nil { panic(fmt.Errorf("failed to open file: %w", err)) } + file.Close() - roots := []struct { + type TestCase struct { path string zero bool - }{ - {"./cairo_zero_hint_tests/", true}, - {"./cairo_zero_file_tests/", true}, - {"./builtin_tests/", true}, + } + roots := []TestCase{ + // {"./cairo_zero_hint_tests/", true}, + // {"./cairo_zero_file_tests/", true}, + // {"./builtin_tests/", true}, // {"./cairo_1_programs/", false}, // {"./cairo_1_programs/dict_non_squashed", false}, + {"./cairo_1_programs/with_input", false}, + } + + inputArgsMap := map[string]string{ + "cairo_1_programs/with_input/array_input_sum__small.cairo": "2 [111 222 333] 1 [444 555 666 777]", + "cairo_1_programs/with_input/array_length__small.cairo": "[1 2 3 4 5 6] [7 8 9 10]", + "cairo_1_programs/with_input/branching.cairo": "123", + "cairo_1_programs/with_input/dict_with_input__small.cairo": "[1 2 3 4]", + "cairo_1_programs/with_input/tensor__small.cairo": "[1 4] [1 5]", } // filter is for debugging purposes @@ -211,22 +222,19 @@ func TestCairoFiles(t *testing.T) { if !filter.filtered(name) { continue } - + inputArgs := inputArgsMap[path] // we run tests concurrently if we don't need benchmarks if !*zerobench { sem <- struct{}{} // acquire a semaphore slot wg.Add(1) - go func(path, name string, root struct { - path string - zero bool - }) { + go func(path, name string, root TestCase, inputArgs string) { defer wg.Done() defer func() { <-sem }() // release the semaphore slot when done - runAndTestFile(t, path, name, benchmarkMap, *zerobench, errorExpected, root.zero) - }(path, name, root) + runAndTestFile(t, path, name, benchmarkMap, *zerobench, errorExpected, root.zero, inputArgs) + }(path, name, root, inputArgs) } else { - runAndTestFile(t, path, name, benchmarkMap, *zerobench, errorExpected, root.zero) + runAndTestFile(t, path, name, benchmarkMap, *zerobench, errorExpected, root.zero, inputArgs) } } } @@ -400,7 +408,7 @@ func runPythonVm(path, layout string) (time.Duration, string, string, error) { // given a path to a compiled cairo zero file, execute it using the // rust vm and return the trace and memory files location -func runRustVm(path, layout string, zero bool) (time.Duration, string, string, error) { +func runRustVm(path, layout string, zero bool, inputArgs string) (time.Duration, string, string, error) { traceOutput := swapExtenstion(path, rsTraceSuffix) memoryOutput := swapExtenstion(path, rsMemorySuffix) @@ -412,6 +420,8 @@ func runRustVm(path, layout string, zero bool) (time.Duration, string, string, e memoryOutput, "--layout", layout, + "--args", + inputArgs, } if zero { @@ -441,7 +451,7 @@ func runRustVm(path, layout string, zero bool) (time.Duration, string, string, e // given a path to a compiled cairo zero file, execute // it using our vm -func runVm(path, layout string, zero bool) (time.Duration, string, string, string, error) { +func runVm(path, layout string, zero bool, inputArgs string) (time.Duration, string, string, string, error) { traceOutput := swapExtenstion(path, traceSuffix) memoryOutput := swapExtenstion(path, memorySuffix) @@ -471,6 +481,8 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin layout, "--available_gas", "9999999", + "--args", + inputArgs, } } args = append(args, path) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 225f92666..bca16d24b 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -59,11 +59,25 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru }, nil } -func AssembleProgram(cairoProgram *starknet.StarknetProgram) (Program, map[uint64][]hinter.Hinter, error) { +func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet.CairoFuncArgs) (Program, map[uint64][]hinter.Hinter, error) { mainFunc, ok := cairoProgram.EntryPointsByFunction["main"] if !ok { return Program{}, nil, fmt.Errorf("cannot find main function") } + expectedArgsSize, actualArgsSize := 0, 0 + for _, arg := range mainFunc.InputArgs { + expectedArgsSize += arg.Size + } + for _, arg := range userArgs { + if arg.Single != nil { + actualArgsSize += 1 + } else { + actualArgsSize += 2 + } + } + if expectedArgsSize != actualArgsSize { + return Program{}, nil, fmt.Errorf("missing arguments for main function") + } program, err := LoadCairoProgram(cairoProgram) if err != nil { return Program{}, nil, fmt.Errorf("cannot load program: %w", err) @@ -603,16 +617,15 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo usedArgs += 1 } } + offset := apOffset - usedArgs for _, param := range paramTypes { - offset := apOffset - usedArgs for i := 0; i < param.Size; i++ { ctx.AddInlineCASM( fmt.Sprintf("[ap + 0] = [ap - %d], ap++;", offset), ) - apOffset += param.Size - usedArgs += param.Size } } + _, endInstructionsSize, err := assembler.CasmToBytecode("call rel 0; ret;") if err != nil { return nil, nil, err From fc09b667d6b7291a80bde640ce125772bfc386a7 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Mon, 13 Jan 2025 22:28:43 +0100 Subject: [PATCH 18/31] Almost all pass --- cmd/cli/main.go | 11 +--- integration_tests/.env | 2 +- integration_tests/cairo_vm_test.go | 4 +- pkg/hintrunner/core/hint.go | 32 +++++++++--- pkg/hintrunner/hintrunner.go | 7 ++- pkg/runner/runner.go | 80 ++++++++++++++++++++---------- 6 files changed, 89 insertions(+), 47 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 5a2c858ca..c8f930a1f 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -12,7 +12,6 @@ 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" ) @@ -221,7 +220,7 @@ func main() { if err != nil { return fmt.Errorf("cannot parse args: %w", err) } - program, hints, err := runner.AssembleProgram(cairoProgram, userArgs) + program, hints, userArgs, err := runner.AssembleProgram(cairoProgram, userArgs, availableGas) if err != nil { return fmt.Errorf("cannot assemble program: %w", err) } @@ -229,14 +228,6 @@ func main() { if proofmode { runnerMode = runner.ProofModeCairo } - 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) }, }, diff --git a/integration_tests/.env b/integration_tests/.env index 5448ff22e..47340bde8 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=poseidon_pedersen__starknet.cairo \ No newline at end of file diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index 65e6bfd63..2ccf9aec2 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -181,8 +181,8 @@ func TestCairoFiles(t *testing.T) { // {"./cairo_zero_hint_tests/", true}, // {"./cairo_zero_file_tests/", true}, // {"./builtin_tests/", true}, - // {"./cairo_1_programs/", false}, - // {"./cairo_1_programs/dict_non_squashed", false}, + {"./cairo_1_programs/", false}, + {"./cairo_1_programs/dict_non_squashed", false}, {"./cairo_1_programs/with_input", false}, } diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index dbc26392f..c43257aff 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1938,29 +1938,47 @@ func (hint *ExternalWriteArgsToMemory) String() string { } func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { - userArgsVar, err := ctx.ScopeManager.GetVariableValue("userArgs") + userArgs, err := hinter.GetVariableAs[[]starknet.CairoFuncArgs](&ctx.ScopeManager, "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") + apOffset, err := hinter.GetVariableAs[uint64](&ctx.ScopeManager, "apOffset") + if err != nil { + return fmt.Errorf("get ap offset: %v", err) } + fmt.Println("apOffset", apOffset, "ap.offset", vm.Context.Ap) + apOffset += vm.Context.Ap for _, arg := range userArgs { if arg.Single != nil { mv := mem.MemoryValueFromFieldElement(arg.Single) - err := vm.Memory.Write(1, vm.Context.Ap, &mv) + err := vm.Memory.Write(1, apOffset, &mv) if err != nil { return fmt.Errorf("write single arg: %v", err) } + apOffset++ } else if arg.Array != nil { arrayBase := vm.Memory.AllocateEmptySegment() mv := mem.MemoryValueFromMemoryAddress(&arrayBase) - err := vm.Memory.Write(1, vm.Context.Ap, &mv) + err := vm.Memory.Write(1, apOffset, &mv) if err != nil { return fmt.Errorf("write array base: %v", err) } - // TODO: Implement array writing + apOffset++ + arrayEnd := arrayBase + for _, val := range arg.Array { + arrayEnd.Offset += 1 + mv := mem.MemoryValueFromFieldElement(&val) + err := vm.Memory.Write(arrayEnd.SegmentIndex, arrayEnd.Offset, &mv) + if err != nil { + return fmt.Errorf("write array element: %v", err) + } + } + mv = mem.MemoryValueFromMemoryAddress(&arrayEnd) + err = vm.Memory.Write(1, apOffset, &mv) + if err != nil { + return fmt.Errorf("write array end: %v", err) + } + apOffset++ } } return nil diff --git a/pkg/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index 92bb64487..f413f4e07 100644 --- a/pkg/hintrunner/hintrunner.go +++ b/pkg/hintrunner/hintrunner.go @@ -15,10 +15,13 @@ type HintRunner struct { hints map[uint64][]h.Hinter } -func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs) HintRunner { +func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs, writeApOffset uint64) HintRunner { context := *h.InitializeDefaultContext() if userArgs != nil { - err := context.ScopeManager.AssignVariable("userArgs", userArgs) + err := context.ScopeManager.AssignVariables(map[string]any{ + "userArgs": userArgs, + "apOffset": writeApOffset, + }) // Error handling: this condition should never be true, since the context was initialized above if err != nil { panic(fmt.Errorf("assign userArgs: %v", err)) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index bca16d24b..b69fdc27d 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -44,11 +44,20 @@ 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, userArgs []starknet.CairoFuncArgs) (Runner, error) { - hintrunner := hintrunner.NewHintRunner(hints, userArgs) layout, err := builtins.GetLayout(layoutName) if err != nil { return Runner{}, err } + writeApOffset := uint64(len(program.Builtins)) + for _, builtin := range program.Builtins { + if builtin == builtins.SegmentArenaType { + writeApOffset += 3 + } + if builtin == builtins.GasBuiltinType { + writeApOffset -= 1 + } + } + hintrunner := hintrunner.NewHintRunner(hints, userArgs, writeApOffset) return Runner{ program: program, runnerMode: runnerMode, @@ -59,10 +68,10 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru }, nil } -func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet.CairoFuncArgs) (Program, map[uint64][]hinter.Hinter, error) { +func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet.CairoFuncArgs, availableGas uint64) (Program, map[uint64][]hinter.Hinter, []starknet.CairoFuncArgs, error) { mainFunc, ok := cairoProgram.EntryPointsByFunction["main"] if !ok { - return Program{}, nil, fmt.Errorf("cannot find main function") + return Program{}, nil, nil, fmt.Errorf("cannot find main function") } expectedArgsSize, actualArgsSize := 0, 0 for _, arg := range mainFunc.InputArgs { @@ -76,19 +85,35 @@ func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet } } if expectedArgsSize != actualArgsSize { - return Program{}, nil, fmt.Errorf("missing arguments for main function") + return Program{}, nil, nil, fmt.Errorf("missing arguments for main function") } program, err := LoadCairoProgram(cairoProgram) if err != nil { - return Program{}, nil, fmt.Errorf("cannot load program: %w", err) + return Program{}, nil, nil, fmt.Errorf("cannot load program: %w", err) + } + gotGasBuiltin := false + for _, b := range program.Builtins { + if b == builtins.GasBuiltinType { + gotGasBuiltin = true + break + } + } + if availableGas > 0 && gotGasBuiltin { + // The first argument is the available gas + availableGasArg := starknet.CairoFuncArgs{ + Single: new(fp.Element).SetUint64(availableGas), + Array: nil, + } + userArgs = append([]starknet.CairoFuncArgs{availableGasArg}, userArgs...) } hints, err := core.GetCairoHints(cairoProgram) if err != nil { - return Program{}, nil, fmt.Errorf("cannot get hints: %w", err) + return Program{}, nil, nil, fmt.Errorf("cannot get hints: %w", err) } - entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc, false) + + entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc, gotGasBuiltin) if err != nil { - return Program{}, nil, fmt.Errorf("cannot load entry code instructions: %w", err) + return Program{}, nil, nil, fmt.Errorf("cannot load entry code instructions: %w", err) } program.Bytecode = append(entryCodeInstructions, program.Bytecode...) program.Bytecode = append(program.Bytecode, GetFooterInstructions()...) @@ -101,7 +126,7 @@ func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet for key, hint := range entryCodeHints { shiftedHintsMap[key] = hint } - return *program, shiftedHintsMap, nil + return *program, shiftedHintsMap, userArgs, nil } // RunEntryPoint is like Run, but it executes the program starting from the given PC offset. @@ -329,7 +354,12 @@ func (runner *Runner) initializeVm( // run until the program counter equals the `pc` parameter func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { + // runner.vm.PrintMemory() for !runner.vm.Context.Pc.Equal(pc) { + fmt.Println("pc", runner.vm.Context.Pc, "ap", runner.vm.Context.Ap, "fp", runner.vm.Context.Fp) + if runner.vm.Context.Pc.Offset == 102 { + runner.vm.PrintMemory() + } if runner.steps() >= runner.maxsteps { return fmt.Errorf( "pc %s step %d: max step limit exceeded (%d)", @@ -531,7 +561,7 @@ func (ctx *InlineCasmContext) AddInlineCASM(code string) { ctx.currentCodeOffset += int(total_size) } -func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeForProof bool) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { +func GetEntryCodeInstructions(function starknet.EntryPointByFunction, gotGasBuiltin bool) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { paramTypes := function.InputArgs apOffset := 0 builtinOffset := 3 @@ -594,8 +624,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo paramsSize += param.Size } apOffset += paramsSize - usedArgs := 0 - for _, builtin := range function.Builtins { if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( @@ -608,22 +636,24 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo fmt.Sprintf("[ap + 0] = [ap - %d] + 3, ap++;", offset), ) apOffset += 1 - } else if builtin == builtins.GasBuiltinType { - hints[uint64(ctx.currentCodeOffset)] = []hinter.Hinter{ - &core.ExternalWriteArgsToMemory{}, - } - ctx.AddInlineCASM("ap += 1;") - apOffset += 1 - usedArgs += 1 } } - offset := apOffset - usedArgs + + if gotGasBuiltin { + ctx.AddInlineCASM("ap += 1;") + apOffset += 1 + } + for _, param := range paramTypes { - for i := 0; i < param.Size; i++ { - ctx.AddInlineCASM( - fmt.Sprintf("[ap + 0] = [ap - %d], ap++;", offset), - ) - } + ctx.AddInlineCASM( + fmt.Sprintf("ap+=%d;", param.Size), + ) + } + + if gotGasBuiltin || paramsSize > 0 { + hints[uint64(0)] = append(hints[uint64(0)], []hinter.Hinter{ + &core.ExternalWriteArgsToMemory{}, + }...) } _, endInstructionsSize, err := assembler.CasmToBytecode("call rel 0; ret;") From 72b3c14da5cc9ee76945fed56d1d0d34587f59ff Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Mon, 13 Jan 2025 22:40:49 +0100 Subject: [PATCH 19/31] Fix lint and unit tests --- pkg/hintrunner/hintrunner_test.go | 4 ++-- pkg/runner/runner.go | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/hintrunner/hintrunner_test.go b/pkg/hintrunner/hintrunner_test.go index c3cbbd5ed..4df00f8e3 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) + }, nil, 0) 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) + }, nil, 0) vm.Context.Pc = memory.MemoryAddress{ SegmentIndex: 0, diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index b69fdc27d..5c4cf94ef 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -641,7 +641,6 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, gotGasBuil if gotGasBuiltin { ctx.AddInlineCASM("ap += 1;") - apOffset += 1 } for _, param := range paramTypes { From ce93d4fb74c1dd198240c0d4999920e881d30510 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 15 Jan 2025 16:00:10 +0100 Subject: [PATCH 20/31] Fix loading gas to the memory as an argument --- cmd/cli/main.go | 7 ++--- integration_tests/.env | 2 +- pkg/hintrunner/core/hint.go | 20 ++++++++++++++- pkg/hintrunner/hintrunner.go | 18 ++++++------- pkg/hintrunner/hintrunner_test.go | 4 +-- pkg/runner/gas.go | 2 +- pkg/runner/runner.go | 40 +++++++---------------------- pkg/runner/runner_benchmark_test.go | 2 +- pkg/runner/runner_test.go | 8 +++--- 9 files changed, 49 insertions(+), 54 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index c8f930a1f..5fcc252e3 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -124,7 +124,7 @@ func main() { if proofmode { runnerMode = runner.ProofModeZero } - return runVM(*program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode, nil) + return runVM(*program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode, nil, 0) }, }, { @@ -228,7 +228,7 @@ func main() { if proofmode { runnerMode = runner.ProofModeCairo } - return runVM(program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode, userArgs) + return runVM(program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode, userArgs, availableGas) }, }, }, @@ -255,9 +255,10 @@ func runVM( hints map[uint64][]hinter.Hinter, runnerMode runner.RunnerMode, userArgs []starknet.CairoFuncArgs, + availableGas uint64, ) error { fmt.Println("Running....") - runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName, userArgs) + runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName, userArgs, availableGas) if err != nil { return fmt.Errorf("cannot create runner: %w", err) } diff --git a/integration_tests/.env b/integration_tests/.env index 47340bde8..5448ff22e 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=poseidon_pedersen__starknet.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 c43257aff..e4ca9aebb 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1946,7 +1946,6 @@ func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinte if err != nil { return fmt.Errorf("get ap offset: %v", err) } - fmt.Println("apOffset", apOffset, "ap.offset", vm.Context.Ap) apOffset += vm.Context.Ap for _, arg := range userArgs { if arg.Single != nil { @@ -1983,3 +1982,22 @@ func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinte } return nil } + +type ExternalWriteGasToMemory struct{} + +func (hint *ExternalWriteGasToMemory) String() string { + return "ExternalWriteGasToMemory" +} + +func (hint *ExternalWriteGasToMemory) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { + gas, err := hinter.GetVariableAs[uint64](&ctx.ScopeManager, "gas") + if err != nil { + return fmt.Errorf("get gas: %v", err) + } + gasVal := mem.MemoryValueFromUint(gas) + err = vm.Memory.Write(1, vm.Context.Ap, &gasVal) + if err != nil { + return fmt.Errorf("write gas: %v", err) + } + return nil +} diff --git a/pkg/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index f413f4e07..ef77e2a01 100644 --- a/pkg/hintrunner/hintrunner.go +++ b/pkg/hintrunner/hintrunner.go @@ -15,17 +15,15 @@ type HintRunner struct { hints map[uint64][]h.Hinter } -func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs, writeApOffset uint64) HintRunner { +func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs, writeApOffset uint64, availableGas uint64) HintRunner { context := *h.InitializeDefaultContext() - if userArgs != nil { - err := context.ScopeManager.AssignVariables(map[string]any{ - "userArgs": userArgs, - "apOffset": writeApOffset, - }) - // Error handling: this condition should never be true, since the context was initialized above - if err != nil { - panic(fmt.Errorf("assign userArgs: %v", err)) - } + err := context.ScopeManager.AssignVariables(map[string]any{ + "userArgs": userArgs, + "apOffset": writeApOffset, + "gas": availableGas, + }) + if err != nil { + panic(fmt.Sprintf("assign variables: %v", err)) } return HintRunner{ // Context for certain hints that require it. Each manager is diff --git a/pkg/hintrunner/hintrunner_test.go b/pkg/hintrunner/hintrunner_test.go index 4df00f8e3..97e7a5ca8 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, 0) + }, nil, 0, 0) 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, 0) + }, nil, 0, 0) vm.Context.Pc = memory.MemoryAddress{ SegmentIndex: 0, diff --git a/pkg/runner/gas.go b/pkg/runner/gas.go index 7e75670e4..57446b8ac 100644 --- a/pkg/runner/gas.go +++ b/pkg/runner/gas.go @@ -51,7 +51,7 @@ func gasInitialization(memory *mem.Memory) error { return err } - preCostTokenTypes := []TokenGasCost{PedersenToken, PoseidonToken, BitwiseToken, EcOpToken, AddModToken, MulModToken} + preCostTokenTypes := []TokenGasCost{PedersenToken, BitwiseToken, EcOpToken, PoseidonToken, AddModToken, MulModToken} for _, token := range preCostTokenTypes { cost, err := getTokenGasCost(token) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 5c4cf94ef..a7b4fdafe 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -43,7 +43,7 @@ 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, userArgs []starknet.CairoFuncArgs) (Runner, error) { +func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode RunnerMode, collectTrace bool, maxsteps uint64, layoutName string, userArgs []starknet.CairoFuncArgs, availableGas uint64) (Runner, error) { layout, err := builtins.GetLayout(layoutName) if err != nil { return Runner{}, err @@ -53,11 +53,8 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru if builtin == builtins.SegmentArenaType { writeApOffset += 3 } - if builtin == builtins.GasBuiltinType { - writeApOffset -= 1 - } } - hintrunner := hintrunner.NewHintRunner(hints, userArgs, writeApOffset) + hintrunner := hintrunner.NewHintRunner(hints, userArgs, writeApOffset, availableGas) return Runner{ program: program, runnerMode: runnerMode, @@ -91,27 +88,12 @@ func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet if err != nil { return Program{}, nil, nil, fmt.Errorf("cannot load program: %w", err) } - gotGasBuiltin := false - for _, b := range program.Builtins { - if b == builtins.GasBuiltinType { - gotGasBuiltin = true - break - } - } - if availableGas > 0 && gotGasBuiltin { - // The first argument is the available gas - availableGasArg := starknet.CairoFuncArgs{ - Single: new(fp.Element).SetUint64(availableGas), - Array: nil, - } - userArgs = append([]starknet.CairoFuncArgs{availableGasArg}, userArgs...) - } hints, err := core.GetCairoHints(cairoProgram) if err != nil { return Program{}, nil, nil, fmt.Errorf("cannot get hints: %w", err) } - entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc, gotGasBuiltin) + entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc) if err != nil { return Program{}, nil, nil, fmt.Errorf("cannot load entry code instructions: %w", err) } @@ -354,12 +336,8 @@ func (runner *Runner) initializeVm( // run until the program counter equals the `pc` parameter func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { - // runner.vm.PrintMemory() for !runner.vm.Context.Pc.Equal(pc) { fmt.Println("pc", runner.vm.Context.Pc, "ap", runner.vm.Context.Ap, "fp", runner.vm.Context.Fp) - if runner.vm.Context.Pc.Offset == 102 { - runner.vm.PrintMemory() - } if runner.steps() >= runner.maxsteps { return fmt.Errorf( "pc %s step %d: max step limit exceeded (%d)", @@ -561,7 +539,7 @@ func (ctx *InlineCasmContext) AddInlineCASM(code string) { ctx.currentCodeOffset += int(total_size) } -func GetEntryCodeInstructions(function starknet.EntryPointByFunction, gotGasBuiltin bool) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { +func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { paramTypes := function.InputArgs apOffset := 0 builtinOffset := 3 @@ -636,20 +614,20 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, gotGasBuil fmt.Sprintf("[ap + 0] = [ap - %d] + 3, ap++;", offset), ) apOffset += 1 + } else if builtin == builtins.GasBuiltinType { + hints[uint64(ctx.currentCodeOffset)] = append(hints[uint64(ctx.currentCodeOffset)], &core.ExternalWriteGasToMemory{}) + ctx.AddInlineCASM("ap += 1;") + apOffset += 1 } } - if gotGasBuiltin { - ctx.AddInlineCASM("ap += 1;") - } - for _, param := range paramTypes { ctx.AddInlineCASM( fmt.Sprintf("ap+=%d;", param.Size), ) } - if gotGasBuiltin || paramsSize > 0 { + if paramsSize > 0 { hints[uint64(0)] = append(hints[uint64(0)], []hinter.Hinter{ &core.ExternalWriteArgsToMemory{}, }...) diff --git a/pkg/runner/runner_benchmark_test.go b/pkg/runner/runner_benchmark_test.go index 6347640e6..3903e7009 100644 --- a/pkg/runner/runner_benchmark_test.go +++ b/pkg/runner/runner_benchmark_test.go @@ -233,7 +233,7 @@ func BenchmarkRunnerWithFibonacci(b *testing.B) { panic(err) } - runner, err := NewRunner(program, hints, ProofModeZero, false, math.MaxUint64, "plain", nil) + runner, err := NewRunner(program, hints, ProofModeZero, false, math.MaxUint64, "plain", nil, 0) if err != nil { panic(err) } diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 50ded3f4d..f7c2e1117 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -27,7 +27,7 @@ func TestSimpleProgram(t *testing.T) { `) hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ExecutionModeZero, false, math.MaxUint64, "plain", nil) + runner, err := NewRunner(program, hints, ExecutionModeZero, false, math.MaxUint64, "plain", nil, 0) require.NoError(t, err) endPc, err := runner.initializeMainEntrypoint() @@ -74,7 +74,7 @@ func TestStepLimitExceeded(t *testing.T) { `) hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ExecutionModeZero, false, 3, "plain", nil) + runner, err := NewRunner(program, hints, ExecutionModeZero, false, 3, "plain", nil, 0) require.NoError(t, err) endPc, err := runner.initializeMainEntrypoint() @@ -133,7 +133,7 @@ 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, ProofModeZero, false, uint64(maxstep), "plain", nil) + runner, err := NewRunner(program, hints, ProofModeZero, false, uint64(maxstep), "plain", nil, 0) require.NoError(t, err) err = runner.Run() @@ -435,7 +435,7 @@ func TestModuloBuiltin(t *testing.T) { func createRunner(code string, layoutName string, builtins ...builtins.BuiltinType) Runner { program := createProgramWithBuiltins(code, builtins...) hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ExecutionModeZero, false, math.MaxUint64, layoutName, nil) + runner, err := NewRunner(program, hints, ExecutionModeZero, false, math.MaxUint64, layoutName, nil, 0) if err != nil { panic(err) } From 734d1e73d379955e7c56ca3c1ee8b19c7d867fdd Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Thu, 16 Jan 2025 13:50:31 +0100 Subject: [PATCH 21/31] Fix run for tensor__small.cairo --- pkg/hintrunner/core/hint.go | 2 +- pkg/runner/runner.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index e4ca9aebb..c51d2c5b7 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1965,12 +1965,12 @@ func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinte apOffset++ arrayEnd := arrayBase for _, val := range arg.Array { - arrayEnd.Offset += 1 mv := mem.MemoryValueFromFieldElement(&val) err := vm.Memory.Write(arrayEnd.SegmentIndex, arrayEnd.Offset, &mv) if err != nil { return fmt.Errorf("write array element: %v", err) } + arrayEnd.Offset += 1 } mv = mem.MemoryValueFromMemoryAddress(&arrayEnd) err = vm.Memory.Write(1, apOffset, &mv) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index a7b4fdafe..4e498b6c6 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -337,7 +337,6 @@ func (runner *Runner) initializeVm( // run until the program counter equals the `pc` parameter func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { for !runner.vm.Context.Pc.Equal(pc) { - fmt.Println("pc", runner.vm.Context.Pc, "ap", runner.vm.Context.Ap, "fp", runner.vm.Context.Fp) if runner.steps() >= runner.maxsteps { return fmt.Errorf( "pc %s step %d: max step limit exceeded (%d)", From c5113293d74a357593b07cc719d3324fb7e27f05 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 17 Jan 2025 12:39:52 +0100 Subject: [PATCH 22/31] check for input and output types for proofmode --- cmd/cli/main.go | 2 +- integration_tests/.env | 2 +- .../with_input/proofmode__small.cairo | 22 +++++++++++++++++ integration_tests/cairo_vm_test.go | 3 ++- pkg/runner/runner.go | 24 ++++++++++++++++++- 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 integration_tests/cairo_1_programs/with_input/proofmode__small.cairo diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 5fcc252e3..448198ef5 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -220,7 +220,7 @@ func main() { if err != nil { return fmt.Errorf("cannot parse args: %w", err) } - program, hints, userArgs, err := runner.AssembleProgram(cairoProgram, userArgs, availableGas) + program, hints, userArgs, err := runner.AssembleProgram(cairoProgram, userArgs, availableGas, proofmode) if err != nil { return fmt.Errorf("cannot assemble program: %w", err) } diff --git a/integration_tests/.env b/integration_tests/.env index 5448ff22e..8c90ed93f 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=proofmode__small.cairo \ No newline at end of file diff --git a/integration_tests/cairo_1_programs/with_input/proofmode__small.cairo b/integration_tests/cairo_1_programs/with_input/proofmode__small.cairo new file mode 100644 index 000000000..0d299e431 --- /dev/null +++ b/integration_tests/cairo_1_programs/with_input/proofmode__small.cairo @@ -0,0 +1,22 @@ +fn arr_sum(mut vals: Array) -> felt252 { + let mut sum: felt252 = 0; + + loop { + match vals.pop_front() { + Option::Some(v) => { + sum += v; + }, + Option::None(_) => { + break sum; + } + }; + } +} + +fn main(vals: Array) -> Array { + let sum1 = arr_sum(vals); + let mut res: Array = ArrayTrait::new(); + res.append(sum1); + res.append(sum1); + res +} \ No newline at end of file diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index 2ccf9aec2..eca3f1bfe 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -192,6 +192,7 @@ func TestCairoFiles(t *testing.T) { "cairo_1_programs/with_input/branching.cairo": "123", "cairo_1_programs/with_input/dict_with_input__small.cairo": "[1 2 3 4]", "cairo_1_programs/with_input/tensor__small.cairo": "[1 4] [1 5]", + "cairo_1_programs/with_input/proofmode__small.cairo": "[1 2 3 4 5]", } // filter is for debugging purposes @@ -472,7 +473,7 @@ func runVm(path, layout string, zero bool, inputArgs string) (time.Duration, str if !zero { args = []string{ cliCommand, - // "--proofmode", + "--proofmode", "--tracefile", traceOutput, "--memoryfile", diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 7538765da..4355923de 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -65,11 +65,17 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru }, nil } -func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet.CairoFuncArgs, availableGas uint64) (Program, map[uint64][]hinter.Hinter, []starknet.CairoFuncArgs, error) { +func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet.CairoFuncArgs, availableGas uint64, proofmode bool) (Program, map[uint64][]hinter.Hinter, []starknet.CairoFuncArgs, error) { mainFunc, ok := cairoProgram.EntryPointsByFunction["main"] if !ok { return Program{}, nil, nil, fmt.Errorf("cannot find main function") } + if proofmode { + err := CheckOnlyArrayFeltInputAndReturntValue(mainFunc) + if err != nil { + return Program{}, nil, nil, err + } + } expectedArgsSize, actualArgsSize := 0, 0 for _, arg := range mainFunc.InputArgs { expectedArgsSize += arg.Size @@ -647,3 +653,19 @@ func GetFooterInstructions() []*fp.Element { // and `pc` registers. return []*fp.Element{new(fp.Element).SetUint64(2345108766317314046)} } + +func CheckOnlyArrayFeltInputAndReturntValue(mainFunc starknet.EntryPointByFunction) error { + if len(mainFunc.InputArgs) != 1 { + return fmt.Errorf("main function should have only one input argument") + } + if len(mainFunc.ReturnArg) != 1 { + return fmt.Errorf("main function should have only one return argument") + } + if mainFunc.InputArgs[0].Size != 2 || mainFunc.InputArgs[0].DebugName != "Array" { + return fmt.Errorf("main function input argument should be Felt Array") + } + if mainFunc.ReturnArg[0].Size != 3 || mainFunc.ReturnArg[0].DebugName != "core::panics::PanicResult::<(core::array::Array::)>" { + return fmt.Errorf("main function return argument should be Felt Array") + } + return nil +} From 74c21109e63d6a03438f0e1b5126313128a45813 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Thu, 23 Jan 2025 15:21:29 +0100 Subject: [PATCH 23/31] Implementation for entry code --- pkg/parsers/starknet/starknet.go | 8 +-- pkg/runner/runner.go | 116 +++++++++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 10 deletions(-) diff --git a/pkg/parsers/starknet/starknet.go b/pkg/parsers/starknet/starknet.go index 0bbc615d5..9c3f49c85 100644 --- a/pkg/parsers/starknet/starknet.go +++ b/pkg/parsers/starknet/starknet.go @@ -28,10 +28,10 @@ type Arg struct { } type EntryPointByFunction struct { - Offset int `json:"offset"` - Builtins []builtins.BuiltinType `json:"builtins"` - InputArgs []Arg `json:"input_args"` - ReturnArg []Arg `json:"return_arg"` + Offset int `json:"offset"` + Builtins []builtins.BuiltinType `json:"builtins"` + InputArgs []Arg `json:"input_args"` + ReturnArgs []Arg `json:"return_arg"` } type Hints struct { diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 4355923de..82f082dc3 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "slices" + "strings" "github.com/NethermindEth/cairo-vm-go/pkg/assembler" "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner" @@ -99,7 +100,7 @@ func AssembleProgram(cairoProgram *starknet.StarknetProgram, userArgs []starknet return Program{}, nil, nil, fmt.Errorf("cannot get hints: %w", err) } - entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc) + entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc, proofmode) if err != nil { return Program{}, nil, nil, fmt.Errorf("cannot load entry code instructions: %w", err) } @@ -544,12 +545,17 @@ func (ctx *InlineCasmContext) AddInlineCASM(code string) { ctx.currentCodeOffset += int(total_size) } -func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { +// Function derived from the cairo-lang-runner crate. +// https://github.com/starkware-libs/cairo/blob/40a7b60687682238f7f71ef7c59c986cc5733915/crates/cairo-lang-runner/src/lib.rs#L703 +// / Returns the instructions to add to the beginning of the code to successfully call the main +// / function, as well as the builtins required to execute the program. +func GetEntryCodeInstructions(function starknet.EntryPointByFunction, proofmode bool) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { paramTypes := function.InputArgs apOffset := 0 builtinOffset := 3 codeOffset := uint64(function.Offset) builtinsOffsetsMap := map[builtins.BuiltinType]int{} + ctx := &InlineCasmContext{} for _, builtin := range []builtins.BuiltinType{ builtins.MulModType, @@ -567,7 +573,11 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Ele } } - ctx := &InlineCasmContext{} + if proofmode { + builtinsOffsetsMap[builtins.OutputType] = 0 + // Increment the length of the builtins by 1 to account for the output builtin + ctx.AddInlineCASM(fmt.Sprintf("ap += %d;", len(function.Builtins)+1)) + } gotSegmentArena := false for _, builtin := range function.Builtins { @@ -607,7 +617,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Ele paramsSize += param.Size } apOffset += paramsSize - + gotGasBuiltin := false for _, builtin := range function.Builtins { if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( @@ -624,6 +634,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Ele hints[uint64(ctx.currentCodeOffset)] = append(hints[uint64(ctx.currentCodeOffset)], &core.ExternalWriteGasToMemory{}) ctx.AddInlineCASM("ap += 1;") apOffset += 1 + gotGasBuiltin = true } } @@ -645,6 +656,99 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction) ([]*fp.Ele } totalSize := uint64(endInstructionsSize) + uint64(codeOffset) ctx.AddInlineCASM(fmt.Sprintf("call rel %d; ret;", int(totalSize))) + outputPtr := fmt.Sprintf("[fp-%d]", len(function.Builtins)+2) + if proofmode { + for i, b := range function.Builtins { + // assert [fp + i] == [fp - builtin_offset] + offset := builtinsOffsetsMap[b] + ctx.AddInlineCASM(fmt.Sprintf("[fp+%d] = [ap-%d];", i, offset)) + } + outputs := []string{} + + lastReturnArg := function.ReturnArgs[len(function.ReturnArgs)-1] + for i := function.ReturnArgs[len(function.ReturnArgs)-1].Size + 1; i >= 1; i-- { + outputs = append(outputs, fmt.Sprintf("[ap-%d]", i)) + } + + arrayStartPtr, arrayEndPtr := outputs[0], outputs[1] + if strings.HasPrefix(lastReturnArg.DebugName, "core::panics::PanicResult") { + // assert panic_flag = *(output_ptr++); + panicFlag := outputs[0] + ctx.AddInlineCASM(fmt.Sprintf("%s = [%s];", panicFlag, outputPtr)) + arrayStartPtr, arrayEndPtr = outputs[1], outputs[2] + } + + ctx.AddInlineCASM( + fmt.Sprintf(` + %s = [ap] + %s, ap++; + [ap-1] = [%s+1]; + [ap] = [ap-1], ap++; + [ap] = %s, ap++; + [ap] = %s + 1, ap++; + jmp rel 4 if [ap-3] != 0; + jmp rel 12; + + [ap] = [[ap-2]], ap++; + [ap-1] = [[ap-2]]; + [ap-4] = [ap] + 1, ap++; + [ap] = [ap-4] + 1, ap++; + [ap] = [ap-4] + 1, ap++; + jmp rel -8 if [ap-3] != 0; + `, arrayEndPtr, arrayStartPtr, outputPtr, arrayStartPtr, outputPtr), + ) + if paramsSize != 0 { + offset := 2 * len(function.Builtins) + if gotSegmentArena { + offset += 4 + } + if gotGasBuiltin { + offset += 1 + } + arrayStartPtr := fmt.Sprintf("[fp+%d]", offset) + arrayEndPtr := fmt.Sprintf("[fp+%d]", offset+1) + + ctx.AddInlineCASM( + fmt.Sprintf(` + [%s] = [ap] + %s, ap++; + [ap-1] = [[ap-2]]; + [ap] = [ap-1], ap++; + [ap] = %s, ap++; + [ap] = [ap-4]+1, ap++; + jmp rel 4 if [ap-3] != 0; + jmp rel 12; + + [ap] = [[ap-2]], ap++; + [ap-1] = [[ap-2]]; + [ap-4] = [ap] + 1, ap++; + [ap] = [ap-4] + 1, ap++; + [ap] = [ap-4] + 1; + jmp rel -8 if [ap-3] != 0; + `, arrayEndPtr, arrayStartPtr, arrayStartPtr), + ) + } + // After we are done writing into the output segment, we can write the final output_ptr into locals: + // The last instruction will write the final output ptr so we can find it in [ap - 1] + ctx.AddInlineCASM("[fp] = [[ap - 1]];") + + if gotSegmentArena { + offset := 2 + len(function.Builtins) + segmentArenaPtr := fmt.Sprintf("[fp + %d]", offset) + fmt.Println(segmentArenaPtr) + } + + // Copying the final builtins from locals into the top of the stack. + for i := range function.Builtins { + ctx.AddInlineCASM(fmt.Sprintf("[ap] = [fp + %d], ap++;", i)) + } + } else { + // TODO: Writing the final builtins into the top of the stack. + + } + if proofmode { + ctx.AddInlineCASM("jmp rel 0;") + } else { + ctx.AddInlineCASM("ret;") + } return ctx.instructions, hints, nil } @@ -658,13 +762,13 @@ func CheckOnlyArrayFeltInputAndReturntValue(mainFunc starknet.EntryPointByFuncti if len(mainFunc.InputArgs) != 1 { return fmt.Errorf("main function should have only one input argument") } - if len(mainFunc.ReturnArg) != 1 { + if len(mainFunc.ReturnArgs) != 1 { return fmt.Errorf("main function should have only one return argument") } if mainFunc.InputArgs[0].Size != 2 || mainFunc.InputArgs[0].DebugName != "Array" { return fmt.Errorf("main function input argument should be Felt Array") } - if mainFunc.ReturnArg[0].Size != 3 || mainFunc.ReturnArg[0].DebugName != "core::panics::PanicResult::<(core::array::Array::)>" { + if mainFunc.ReturnArgs[0].Size != 3 || mainFunc.ReturnArgs[0].DebugName != "core::panics::PanicResult::<(core::array::Array::)>" { return fmt.Errorf("main function return argument should be Felt Array") } return nil From 688306098cc434874559fb9f2f5bfb5a3903f094 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Thu, 23 Jan 2025 19:37:08 +0100 Subject: [PATCH 24/31] Implement inline casm for segment arena --- integration_tests/.env | 2 +- .../proofmode_segment_arena__small.cairo | 45 ++++++++++++++++++ .../proofmode_with_builtins__small.cairo | 47 +++++++++++++++++++ integration_tests/cairo_vm_test.go | 14 +++--- pkg/runner/runner.go | 30 +++++++++--- 5 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 integration_tests/cairo_1_programs/with_input/proofmode_segment_arena__small.cairo create mode 100644 integration_tests/cairo_1_programs/with_input/proofmode_with_builtins__small.cairo diff --git a/integration_tests/.env b/integration_tests/.env index 8c90ed93f..5448ff22e 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=proofmode__small.cairo \ No newline at end of file +INTEGRATION_TESTS_FILTERS= \ No newline at end of file diff --git a/integration_tests/cairo_1_programs/with_input/proofmode_segment_arena__small.cairo b/integration_tests/cairo_1_programs/with_input/proofmode_segment_arena__small.cairo new file mode 100644 index 000000000..6c7ea1e2b --- /dev/null +++ b/integration_tests/cairo_1_programs/with_input/proofmode_segment_arena__small.cairo @@ -0,0 +1,45 @@ +use array::ArrayTrait; +use array::SpanTrait; +use dict::Felt252DictTrait; +use pedersen::PedersenTrait; + +fn main(mut vals: Array) -> Array { + // Create a dictionary using segment arena + let mut dict = felt252_dict_new::(); + + // Store each value in the dictionary with its index + let mut index: felt252 = 0; + loop { + match vals.pop_front() { + Option::Some(v) => { + dict.insert(index, v); + index += 1; + }, + Option::None(_) => { + break; + } + }; + }; + + // Create result array + let mut res: Array = ArrayTrait::new(); + + // Read values back from dictionary and hash them + let mut i: felt252 = 0; + loop { + let i_u128: u128 = i.try_into().unwrap(); + let index_u128: u128 = index.try_into().unwrap(); + + if i_u128 >= index_u128 { + break; + } + + let value = dict.get(i); + let hash = pedersen::pedersen(value, i); + res.append(hash); + + i += 1; + }; + + res +} \ No newline at end of file diff --git a/integration_tests/cairo_1_programs/with_input/proofmode_with_builtins__small.cairo b/integration_tests/cairo_1_programs/with_input/proofmode_with_builtins__small.cairo new file mode 100644 index 000000000..f61d484ff --- /dev/null +++ b/integration_tests/cairo_1_programs/with_input/proofmode_with_builtins__small.cairo @@ -0,0 +1,47 @@ +use array::ArrayTrait; +use array::SpanTrait; +use pedersen::PedersenTrait; +use core::num::traits::Bounded; + +fn check_and_sum(mut vals: Array) -> felt252 { + let mut sum: felt252 = 0; + let upper_bound: u128 = 1000000; // Example bound + + loop { + match vals.pop_front() { + Option::Some(v) => { + // Convert felt252 to u128 for range check + let v_u128: u128 = v.try_into().unwrap(); + // This will ensure v is non-negative and within u128 bounds + assert(v_u128 <= upper_bound, 'Value exceeds bound'); + + sum += v; + }, + Option::None(_) => { + break sum; + } + }; + } +} + +fn main(mut vals: Array) -> Array { + // Calculate sum with range checks + let sum = check_and_sum(vals); + + // Hash the sum using Pedersen + let hash = pedersen::pedersen(sum, 0); + + // Create result array + let mut res: Array = ArrayTrait::new(); + + // Add original sum + res.append(sum); + // Add hash of sum + res.append(hash); + + // Add a u128 conversion to demonstrate more range checks + let sum_u128: u128 = sum.try_into().unwrap(); + res.append(sum_u128.into()); + + res +} \ No newline at end of file diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index eca3f1bfe..e79f47f92 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -187,12 +187,14 @@ func TestCairoFiles(t *testing.T) { } inputArgsMap := map[string]string{ - "cairo_1_programs/with_input/array_input_sum__small.cairo": "2 [111 222 333] 1 [444 555 666 777]", - "cairo_1_programs/with_input/array_length__small.cairo": "[1 2 3 4 5 6] [7 8 9 10]", - "cairo_1_programs/with_input/branching.cairo": "123", - "cairo_1_programs/with_input/dict_with_input__small.cairo": "[1 2 3 4]", - "cairo_1_programs/with_input/tensor__small.cairo": "[1 4] [1 5]", - "cairo_1_programs/with_input/proofmode__small.cairo": "[1 2 3 4 5]", + "cairo_1_programs/with_input/array_input_sum__small.cairo": "2 [111 222 333] 1 [444 555 666 777]", + "cairo_1_programs/with_input/array_length__small.cairo": "[1 2 3 4 5 6] [7 8 9 10]", + "cairo_1_programs/with_input/branching.cairo": "123", + "cairo_1_programs/with_input/dict_with_input__small.cairo": "[1 2 3 4]", + "cairo_1_programs/with_input/tensor__small.cairo": "[1 4] [1 5]", + "cairo_1_programs/with_input/proofmode__small.cairo": "[1 2 3 4 5]", + "cairo_1_programs/with_input/proofmode_with_builtins__small.cairo": "[1 2 3 4 5]", + "cairo_1_programs/with_input/proofmode_segment_arena__small.cairo": "[1 2 3 4 5]", } // filter is for debugging purposes diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 82f082dc3..22b322a3e 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -661,7 +661,8 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, proofmode for i, b := range function.Builtins { // assert [fp + i] == [fp - builtin_offset] offset := builtinsOffsetsMap[b] - ctx.AddInlineCASM(fmt.Sprintf("[fp+%d] = [ap-%d];", i, offset)) + // increment the offset by 1 to account for the skipped output builtin + ctx.AddInlineCASM(fmt.Sprintf("[fp+%d] = [ap-%d];", i+1, offset)) } outputs := []string{} @@ -709,7 +710,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, proofmode ctx.AddInlineCASM( fmt.Sprintf(` - [%s] = [ap] + %s, ap++; + %s = [ap] + %s, ap++; [ap-1] = [[ap-2]]; [ap] = [ap-1], ap++; [ap] = %s, ap++; @@ -719,9 +720,9 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, proofmode [ap] = [[ap-2]], ap++; [ap-1] = [[ap-2]]; - [ap-4] = [ap] + 1, ap++; - [ap] = [ap-4] + 1, ap++; - [ap] = [ap-4] + 1; + [ap-4] = [ap]+1, ap++; + [ap] = [ap-4]+1, ap++; + [ap] = [ap-4]+1; jmp rel -8 if [ap-3] != 0; `, arrayEndPtr, arrayStartPtr, arrayStartPtr), ) @@ -733,7 +734,24 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, proofmode if gotSegmentArena { offset := 2 + len(function.Builtins) segmentArenaPtr := fmt.Sprintf("[fp + %d]", offset) - fmt.Println(segmentArenaPtr) + ctx.AddInlineCASM(fmt.Sprintf(` + [ap]=[%s-2], ap++; + [ap]=[%s-1], ap++; + [ap-1]=[ap-2]; + jmp rel 4 if [ap-2] != 0; + jpm rel 19; + [ap]=[[%s-3], ap++; + [ap-3] = [ap]+1; + jmp rel 4 if [ap-1] != 0; + jmp rel 12; + [ap]=[[ap-2]+1], ap++; + [ap] = [[ap-3]+3], ap++; + [ap-1] = [ap-2] + 1; + [ap] = [ap-4] + 3, ap++; + [ap-4] = [ap] + 1, ap++; + jmp rel -12; + `, segmentArenaPtr, segmentArenaPtr, segmentArenaPtr, + )) } // Copying the final builtins from locals into the top of the stack. From 8d5046543e9a319d18c20711952e4eb1bb673d3f Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Thu, 23 Jan 2025 19:51:44 +0100 Subject: [PATCH 25/31] Lint the project --- pkg/hintrunner/hintrunner_test.go | 4 ++-- pkg/runner/runner.go | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/hintrunner/hintrunner_test.go b/pkg/hintrunner/hintrunner_test.go index 97e7a5ca8..c3cbbd5ed 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, 0, 0) + }, 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, 0, 0) + }, nil) vm.Context.Pc = memory.MemoryAddress{ SegmentIndex: 0, diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 75ba3a540..38e75e63a 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -791,7 +791,11 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, proofmode ctx.AddInlineCASM(fmt.Sprintf("[ap] = [fp + %d], ap++;", i)) } } else { - // TODO: Writing the final builtins into the top of the stack. + // Writing the final builtins into the top of the stack. + for _, b := range function.Builtins { + offset := builtinsOffsetsMap[b] + ctx.AddInlineCASM(fmt.Sprintf("[ap] = [fp - %d], ap++;", offset)) + } } if proofmode { From 78d3bbc7c8953b920bc686ac4dbf10ad3d2f65b9 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Thu, 23 Jan 2025 19:52:22 +0100 Subject: [PATCH 26/31] remove system type --- pkg/vm/builtins/builtin_runner.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/vm/builtins/builtin_runner.go b/pkg/vm/builtins/builtin_runner.go index 5a12f2abb..7e58e1bdd 100644 --- a/pkg/vm/builtins/builtin_runner.go +++ b/pkg/vm/builtins/builtin_runner.go @@ -25,7 +25,6 @@ const ( AddModeType MulModType GasBuiltinType - SystemType ) func Runner(name BuiltinType) memory.BuiltinRunner { From eeef9a25d3a341730fbe285e1ebb7f2f28803b41 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Sat, 25 Jan 2025 14:55:21 +0100 Subject: [PATCH 27/31] Modifications for the runner --- cmd/cli/main.go | 29 +++++++++++++++++++---------- integration_tests/cairo_vm_test.go | 3 +-- pkg/runner/runner.go | 11 +++++++---- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 448198ef5..2e419a4a3 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -258,7 +258,7 @@ func runVM( availableGas uint64, ) error { fmt.Println("Running....") - runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName, userArgs, availableGas) + cairoRunner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName, userArgs, availableGas) if err != nil { return fmt.Errorf("cannot create runner: %w", err) } @@ -268,25 +268,34 @@ 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 := cairoRunner.Run(); err != nil { return fmt.Errorf("runtime error: %w", err) } } else { - if err := runner.RunEntryPoint(entrypointOffset); err != nil { + if err := cairoRunner.RunEntryPoint(entrypointOffset); err != nil { return fmt.Errorf("runtime error (entrypoint=%d): %w", entrypointOffset, err) } } + + if airPublicInputLocation != "" { + if runnerMode == runner.ProofModeCairo { + fmt.Println("finalizing builtins for cairo") + } else if runnerMode == runner.ExecutionModeCairo { + fmt.Println("finalizing builtins for cairo") + } + + } if proofmode { - if err := runner.EndRun(); err != nil { + if err := cairoRunner.EndRun(); err != nil { return fmt.Errorf("cannot end run: %w", err) } - if err := runner.FinalizeSegments(); err != nil { + if err := cairoRunner.FinalizeSegments(); err != nil { return fmt.Errorf("cannot finalize segments: %w", err) } } if proofmode || collectTrace { - trace, err := runner.BuildTrace() + trace, err := cairoRunner.BuildTrace() if err != nil { return fmt.Errorf("cannot build trace: %w", err) } @@ -299,7 +308,7 @@ func runVM( } if proofmode || buildMemory { - memory, err := runner.BuildMemory() + memory, err := cairoRunner.BuildMemory() if err != nil { return fmt.Errorf("cannot build memory: %w", err) } @@ -313,7 +322,7 @@ func runVM( if proofmode { if airPublicInputLocation != "" { - airPublicInput, err := runner.GetAirPublicInput() + airPublicInput, err := cairoRunner.GetAirPublicInput() if err != nil { return err } @@ -336,7 +345,7 @@ func runVM( if err != nil { return err } - airPrivateInput, err := runner.GetAirPrivateInput(tracePath, memoryPath) + airPrivateInput, err := cairoRunner.GetAirPrivateInput(tracePath, memoryPath) if err != nil { return err } @@ -352,7 +361,7 @@ func runVM( } fmt.Println("Success!") - output := runner.Output() + output := cairoRunner.Output() if len(output) > 0 { fmt.Println("Program output:") for _, val := range output { diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index c46c97438..6550250fd 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -184,6 +184,7 @@ func TestCairoFiles(t *testing.T) { {"./cairo_1_programs/", false}, {"./cairo_1_programs/dict_non_squashed", false}, {"./cairo_1_programs/with_input", false}, + {"./cairo_1_programs/serialized_output", false}, } inputArgsMap := map[string]string{ @@ -423,8 +424,6 @@ func runRustVm(path, layout string, zero bool, inputArgs string) (time.Duration, memoryOutput, "--layout", layout, - "--args", - inputArgs, } if zero { diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 38e75e63a..48a4be406 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -49,7 +49,7 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru if err != nil { return Runner{}, err } - newHintRunnerContext := getNewHintRunnerContext(program, userArgs, availableGas) + newHintRunnerContext := getNewHintRunnerContext(program, userArgs, availableGas, runnerMode == ProofModeCairo || runnerMode == ProofModeZero) hintrunner := hintrunner.NewHintRunner(hints, &newHintRunnerContext) return Runner{ program: program, @@ -61,7 +61,7 @@ func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode Ru }, nil } -func getNewHintRunnerContext(program *Program, userArgs []starknet.CairoFuncArgs, availableGas uint64) hinter.HintRunnerContext { +func getNewHintRunnerContext(program *Program, userArgs []starknet.CairoFuncArgs, availableGas uint64, proofmode bool) hinter.HintRunnerContext { // The writeApOffset is the offset where the user arguments will be written. It is added to the current AP in the ExternalWriteArgsToMemory hint. // The writeApOffset is significant for Cairo programs, because of the prepended Entry Code instructions. // In the entry code instructions the builtins bases (excluding gas, segment arena and output) are written to the memory, @@ -78,6 +78,9 @@ func getNewHintRunnerContext(program *Program, userArgs []starknet.CairoFuncArgs writeApOffset += 3 } } + if proofmode { + writeApOffset += uint64(len(program.Builtins)) - 1 + } newHintrunnerContext := *hinter.InitializeDefaultContext() err := newHintrunnerContext.ScopeManager.AssignVariables(map[string]any{ @@ -814,10 +817,10 @@ func GetFooterInstructions() []*fp.Element { func CheckOnlyArrayFeltInputAndReturntValue(mainFunc starknet.EntryPointByFunction) error { if len(mainFunc.InputArgs) != 1 { - return fmt.Errorf("main function should have only one input argument") + return fmt.Errorf("main function in proofmode should have felt252 array as input argument") } if len(mainFunc.ReturnArgs) != 1 { - return fmt.Errorf("main function should have only one return argument") + return fmt.Errorf("main function in proofmode should have an felt252 array as return argument") } if mainFunc.InputArgs[0].Size != 2 || mainFunc.InputArgs[0].DebugName != "Array" { return fmt.Errorf("main function input argument should be Felt Array") From b1a2881e0e0752b4543f7383055d833e10fa9a6a Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 29 Jan 2025 12:58:26 +0100 Subject: [PATCH 28/31] Add relocate all dictionaries hint --- pkg/hintrunner/core/hint.go | 11 +++++++++++ pkg/hintrunner/hinter/dict.go | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 6ef697617..b7920181a 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -2009,3 +2009,14 @@ func (hint *ExternalWriteGasToMemory) Execute(vm *VM.VirtualMachine, ctx *hinter } return nil } + +type RelocateAllDictionaries struct{} + +func (hint *RelocateAllDictionaries) String() string { + return "RelocateAllDictionaries" +} + +func (hint *RelocateAllDictionaries) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { + ctx.DictionaryManager.RelocateAllDictionaries(vm) + return nil +} diff --git a/pkg/hintrunner/hinter/dict.go b/pkg/hintrunner/hinter/dict.go index 88f3569c1..516f5a164 100644 --- a/pkg/hintrunner/hinter/dict.go +++ b/pkg/hintrunner/hinter/dict.go @@ -86,6 +86,14 @@ func (dm *DictionaryManager) Set(dictAddr *mem.MemoryAddress, key *f.Element, va return fmt.Errorf("no dictionary at address %s", dictAddr) } +// / Relocates all dictionaries into a single segment if proofmode is enabled +func (dm *DictionaryManager) RelocateAllDictionaries(vm *VM.VirtualMachine) { + for segment, dict := range dm.dictionaries { + dict.Relocate(vm) + + } +} + // Used to keep track of squashed dictionaries type SquashedDictionaryManager struct { // A map from each key to a list of indices where the key is present From 4fc752b4ea64e2ba94182613d5082c37cc6ee969 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 29 Jan 2025 12:58:40 +0100 Subject: [PATCH 29/31] allocate empty segment --- pkg/hintrunner/hinter/dict.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/hintrunner/hinter/dict.go b/pkg/hintrunner/hinter/dict.go index 516f5a164..6cc604605 100644 --- a/pkg/hintrunner/hinter/dict.go +++ b/pkg/hintrunner/hinter/dict.go @@ -86,8 +86,9 @@ func (dm *DictionaryManager) Set(dictAddr *mem.MemoryAddress, key *f.Element, va return fmt.Errorf("no dictionary at address %s", dictAddr) } -// / Relocates all dictionaries into a single segment if proofmode is enabled +// Relocates all dictionaries into a single segment if proofmode is enabled func (dm *DictionaryManager) RelocateAllDictionaries(vm *VM.VirtualMachine) { + segmentAddr := vm.Memory.AllocateEmptySegment() for segment, dict := range dm.dictionaries { dict.Relocate(vm) From 6a71d5d9502cba01f173b28cd4141f0442a84948 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 29 Jan 2025 19:18:21 +0100 Subject: [PATCH 30/31] Implement dict relocation --- pkg/hintrunner/core/hint.go | 6 +++++- pkg/hintrunner/hinter/dict.go | 40 +++++++++++++++++++++++++++++------ pkg/runner/runner.go | 7 +++--- pkg/vm/memory/memory.go | 11 ++++++++++ 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index b7920181a..5fe76d6ca 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1075,7 +1075,11 @@ func (hint *AllocFelt252Dict) String() string { return "AllocFelt252Dict" } func (hint *AllocFelt252Dict) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { - hinter.InitializeDictionaryManager(ctx) + useTemporarySegments, err := hinter.GetVariableAs[bool](&ctx.ScopeManager, "useTemporarySegments") + if err != nil { + return fmt.Errorf("get useTemporarySegments: %w", err) + } + hinter.InitializeDictionaryManager(ctx, useTemporarySegments) arenaPtr, err := hinter.ResolveAsAddress(vm, hint.SegmentArenaPtr) if err != nil { diff --git a/pkg/hintrunner/hinter/dict.go b/pkg/hintrunner/hinter/dict.go index 6cc604605..4390b6f5f 100644 --- a/pkg/hintrunner/hinter/dict.go +++ b/pkg/hintrunner/hinter/dict.go @@ -38,19 +38,27 @@ func (d *Dictionary) InitNumber() uint64 { type DictionaryManager struct { // a map that links a segment index to a dictionary dictionaries map[uint64]Dictionary + // useTemporarySegments is a flag that indicates if the dictionaries should located in temporary segments, and later relocated to memory segments + useTemporarySegments bool } -func InitializeDictionaryManager(ctx *HintRunnerContext) { +func InitializeDictionaryManager(ctx *HintRunnerContext, useTemporarySegments bool) { if ctx.DictionaryManager.dictionaries == nil { ctx.DictionaryManager.dictionaries = make(map[uint64]Dictionary) } + ctx.DictionaryManager.useTemporarySegments = useTemporarySegments } // It creates a new segment which will hold dictionary values. It links this // segment with the current dictionary and returns the address that points // to the start of this segment func (dm *DictionaryManager) NewDictionary(vm *VM.VirtualMachine) mem.MemoryAddress { - newDictAddr := vm.Memory.AllocateEmptySegment() + var newDictAddr mem.MemoryAddress + if dm.useTemporarySegments { + newDictAddr = vm.Memory.AllocateEmptyTemporarySegment() + } else { + newDictAddr = vm.Memory.AllocateEmptySegment() + } dm.dictionaries[newDictAddr.SegmentIndex] = Dictionary{ data: make(map[f.Element]*mem.MemoryValue), idx: uint64(len(dm.dictionaries)), @@ -87,12 +95,32 @@ func (dm *DictionaryManager) Set(dictAddr *mem.MemoryAddress, key *f.Element, va } // Relocates all dictionaries into a single segment if proofmode is enabled -func (dm *DictionaryManager) RelocateAllDictionaries(vm *VM.VirtualMachine) { +// In LambdaClass VM there is add_relocation_rule() used, which is used only to relocate dictionaries / in specific hint. Thus we relocate dictionaries right away. +func (dm *DictionaryManager) RelocateAllDictionaries(vm *VM.VirtualMachine) error { segmentAddr := vm.Memory.AllocateEmptySegment() - for segment, dict := range dm.dictionaries { - dict.Relocate(vm) - + for key, dict := range dm.dictionaries { + for _, value := range dict.data { + if value.IsAddress() { + addr, err := value.MemoryAddress() + if err != nil { + return err + } + element := -key + addr.Offset + mv := mem.MemoryValueFromUint(element) + err = vm.Memory.WriteToAddress(&segmentAddr, &mv) + if err != nil { + return err + } + } else { + err := vm.Memory.WriteToAddress(&segmentAddr, value) + if err != nil { + return err + } + } + segmentAddr.Offset++ + } } + return nil } // Used to keep track of squashed dictionaries diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 48a4be406..87057da9b 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -84,9 +84,10 @@ func getNewHintRunnerContext(program *Program, userArgs []starknet.CairoFuncArgs newHintrunnerContext := *hinter.InitializeDefaultContext() err := newHintrunnerContext.ScopeManager.AssignVariables(map[string]any{ - "userArgs": userArgs, - "apOffset": writeApOffset, - "gas": availableGas, + "userArgs": userArgs, + "apOffset": writeApOffset, + "gas": availableGas, + "useTemporarySegments": proofmode, }) // Error handling: this condition should never be true, since the context was initialized above if err != nil { diff --git a/pkg/vm/memory/memory.go b/pkg/vm/memory/memory.go index 944c5eec3..28e3833c4 100644 --- a/pkg/vm/memory/memory.go +++ b/pkg/vm/memory/memory.go @@ -191,6 +191,8 @@ func (segment *Segment) String() string { // Represents the whole VM memory divided into segments type Memory struct { Segments []*Segment + // TemporarySegments is a map of temporary segments, key is the segment index, value is the segment + TemporarySegments map[uint64]*Segment } // todo(rodro): can the amount of segments be known before hand? @@ -227,6 +229,15 @@ func (memory *Memory) AllocateEmptySegment() MemoryAddress { } } +// Allocates an empty temporary segment and returns its index +func (memory *Memory) AllocateEmptyTemporarySegment() MemoryAddress { + memory.TemporarySegments[uint64(len(memory.TemporarySegments))] = EmptySegment() + return MemoryAddress{ + SegmentIndex: -uint64(len(memory.TemporarySegments)), + Offset: 0, + } +} + // Allocate a Builtin segment func (memory *Memory) AllocateBuiltinSegment(builtinRunner BuiltinRunner) MemoryAddress { builtinSegment := EmptySegment().WithBuiltinRunner(builtinRunner) From 71d2c33400d8eed3ed6ae173b6e5ee6cdb2bbda8 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 29 Jan 2025 19:29:18 +0100 Subject: [PATCH 31/31] Lint project, add hint to the entry code --- pkg/hintrunner/core/hint.go | 5 ++++- pkg/runner/runner.go | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 5fe76d6ca..584c8f504 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -2021,6 +2021,9 @@ func (hint *RelocateAllDictionaries) String() string { } func (hint *RelocateAllDictionaries) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { - ctx.DictionaryManager.RelocateAllDictionaries(vm) + err := ctx.DictionaryManager.RelocateAllDictionaries(vm) + if err != nil { + return fmt.Errorf("relocate all dictionaries: %w", err) + } return nil } diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 87057da9b..0d0a4bd6b 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -770,6 +770,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, proofmode if gotSegmentArena { offset := 2 + len(function.Builtins) segmentArenaPtr := fmt.Sprintf("[fp + %d]", offset) + hints[uint64(ctx.currentCodeOffset)] = append(hints[uint64(ctx.currentCodeOffset)], &core.RelocateAllDictionaries{}) ctx.AddInlineCASM(fmt.Sprintf(` [ap]=[%s-2], ap++; [ap]=[%s-1], ap++;