Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionalities for main function inputs #681

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d7b74e6
Fixes for the generation of entry code, fixes of hints parsing
MaksymMalicki Nov 15, 2024
dfd9ec8
Add modifications to the runner
MaksymMalicki Nov 21, 2024
a7814a1
Merge branch 'main' into add_code_entry
MaksymMalicki Dec 2, 2024
83872d8
Add fixes for the entrycode generation
MaksymMalicki Dec 3, 2024
cd04099
Refactor main CLI, offset the hints indexes by entry code size, load …
MaksymMalicki Dec 10, 2024
5eb27ab
Add available gas and user args (#677)
MaksymMalicki Dec 25, 2024
8812042
Fix the integration tests
MaksymMalicki Dec 26, 2024
78cf788
Fixes in the runner
MaksymMalicki Dec 27, 2024
af47a3d
Fixes in the runner
MaksymMalicki Dec 28, 2024
73f6dd5
Fix the unit tests, uncomment pythonVm execution in integration tests…
MaksymMalicki Dec 28, 2024
17b17df
Add writing tokens gas cost to memory
MaksymMalicki Jan 7, 2025
688b3d2
Proper builtins initialization for cairo mode
MaksymMalicki Jan 8, 2025
f105506
Address comments in the PR
MaksymMalicki Jan 9, 2025
cc82938
Fix bugs regarding dicts
MaksymMalicki Jan 10, 2025
a32add7
Remove prints
MaksymMalicki Jan 10, 2025
07f4016
Merge branch 'main' into missing_dict_functionalities
MaksymMalicki Jan 10, 2025
53d4190
Fixes of the last tests for the dicts
MaksymMalicki Jan 11, 2025
31aeeed
Add dict_non_squashed dir to the integration tests
MaksymMalicki Jan 11, 2025
086011a
Add checks for the matching args size, rename files, modify the integ…
MaksymMalicki Jan 11, 2025
fc09b66
Almost all pass
MaksymMalicki Jan 13, 2025
72b3c14
Fix lint and unit tests
MaksymMalicki Jan 13, 2025
ce93d4f
Fix loading gas to the memory as an argument
MaksymMalicki Jan 15, 2025
734d1e7
Fix run for tensor__small.cairo
MaksymMalicki Jan 16, 2025
b54e9bb
Merge branch 'main' into inputs
MaksymMalicki Jan 16, 2025
fea8144
Bring back cairo0 integration tests
MaksymMalicki Jan 16, 2025
bf3a585
Code refactoring, add comments
MaksymMalicki Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 9 additions & 17 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -125,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)
},
},
{
Expand Down Expand Up @@ -217,27 +216,19 @@ 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, userArgs, err := runner.AssembleProgram(cairoProgram, userArgs, availableGas)
if err != nil {
return fmt.Errorf("cannot assemble program: %w", err)
}
runnerMode := runner.ExecutionModeCairo
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{
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)
return runVM(program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode, userArgs, availableGas)
},
},
},
Expand All @@ -264,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)
}
Expand Down
43 changes: 28 additions & 15 deletions integration_tests/cairo_vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -171,16 +171,28 @@ 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
}{
}
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 is used to provide input arguments to the tests that require them. Whenever the args are needed for the new files, they can simply be added here.
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
Expand Down Expand Up @@ -211,22 +223,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)
}
}
}
Expand Down Expand Up @@ -400,7 +409,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)

Expand All @@ -412,6 +421,8 @@ func runRustVm(path, layout string, zero bool) (time.Duration, string, string, e
memoryOutput,
"--layout",
layout,
"--args",
inputArgs,
}

if zero {
Expand Down Expand Up @@ -441,7 +452,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)

Expand Down Expand Up @@ -471,6 +482,8 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin
layout,
"--available_gas",
"9999999",
"--args",
inputArgs,
}
}
args = append(args, path)
Expand Down
59 changes: 52 additions & 7 deletions pkg/hintrunner/core/hint.go
Original file line number Diff line number Diff line change
Expand Up @@ -1937,30 +1937,75 @@ 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")
// The apOffset is the AP correction, which represents the memory slots taken up by the values created by the entry code instructions.
// It is calculated in the `getNewHintRunnerContext()` method.
apOffset, err := hinter.GetVariableAs[uint64](&ctx.ScopeManager, "apOffset")
if err != nil {
return fmt.Errorf("get ap offset: %v", err)
}
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 {
// The array is stored in memory as follows:
// Each array gets assigned a new segment (the pointer is stored in the arrayBase).
// arrayBase and arrayEnd pointers are written to the Execution Segment consecutively.
// Then, the array elements are written to the newly created array segment.
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 {
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)
if err != nil {
return fmt.Errorf("write array end: %v", err)
}
apOffset++
}
}
return nil
}

type ExternalWriteGasToMemory struct{}

func (hint *ExternalWriteGasToMemory) String() string {
return "ExternalWriteGasToMemory"
}

func (hint *ExternalWriteGasToMemory) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error {
// ExternalWriteGasToMemory is a separate hint, that writes the gas value to the memory.
// The gas value is written to the memory cell reserved by the instruction generated in entry code, which is dependent on the ordering of builtins list.
// Therefore the writing of the gas value to the memory is done in a separate hint.
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just confirming here, gas value is written to the execution segment, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

if err != nil {
return fmt.Errorf("write gas: %v", err)
}
return nil
}
13 changes: 2 additions & 11 deletions pkg/hintrunner/hintrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ 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"
)

Expand All @@ -15,19 +14,11 @@ type HintRunner struct {
hints map[uint64][]h.Hinter
}

func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs) HintRunner {
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to keep this comment, because we usually shouldn't have to use panic instead of handling the error. The fact is that in this case, that line should never be reached, so the comment would be flagging that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brought the comment back

if err != nil {
panic(fmt.Errorf("assign userArgs: %v", err))
}
}
func NewHintRunner(hints map[uint64][]h.Hinter, newHintRunnerContext *h.HintRunnerContext) HintRunner {
return HintRunner{
// Context for certain hints that require it. Each manager is
// initialized only when required by the hint
context: context,
context: *newHintRunnerContext,
hints: hints,
}
}
Expand Down
Loading
Loading