Skip to content

Commit

Permalink
Add available gas and user args (#677)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
MaksymMalicki authored Dec 25, 2024
1 parent cd04099 commit 5eb27ab
Show file tree
Hide file tree
Showing 17 changed files with 326 additions and 76 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ 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
with:
# 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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 18 additions & 20 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
34 changes: 31 additions & 3 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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",
Expand Down Expand Up @@ -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)
},
},
{
Expand Down Expand Up @@ -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)
Expand All @@ -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)
},
},
},
Expand All @@ -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)
}
Expand Down
8 changes: 5 additions & 3 deletions integration_tests/cairo_vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...,
Expand Down
35 changes: 35 additions & 0 deletions pkg/hintrunner/core/hint.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
12 changes: 10 additions & 2 deletions pkg/hintrunner/hintrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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,
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/hintrunner/hintrunner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
56 changes: 56 additions & 0 deletions pkg/parsers/starknet/args.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 5eb27ab

Please sign in to comment.