Skip to content

Commit

Permalink
Add cairo1 runner (#648)
Browse files Browse the repository at this point in the history
* Add cairo1 runner options

* Fix bytecode parsing

* fix program parsing

* Revert "Fix bytecode parsing"

This reverts commit a08a4ea.

* Revert "fix program parsing"

This reverts commit 7b046a3.

* fix program parsing

* Fix bytecode parsing

* Add prints for parsing the instructions

* Clean the PR, add libfunc instructions functionality

* Fixes after merge

* Code cleanups

* Code cleanups

* Code cleanups

* Add create entry code

* Modify the initializeMainEntrypoint function

* Add comment regarding CasmToBytecode function modification
  • Loading branch information
MaksymMalicki authored Nov 4, 2024
1 parent 167dadd commit 212c55b
Show file tree
Hide file tree
Showing 14 changed files with 507 additions and 205 deletions.
335 changes: 219 additions & 116 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,6 @@ func main() {
},
},
Action: func(ctx *cli.Context) error {
// TODO: move this action's body to a separate function to decrease the
// code nesting significantly.

pathToFile := ctx.Args().Get(0)
if pathToFile == "" {
return fmt.Errorf("path to cairo file not set")
Expand All @@ -114,132 +111,117 @@ func main() {
if err != nil {
return fmt.Errorf("cannot load program: %w", err)
}

var hints map[uint64][]hinter.Hinter
if zeroProgram.CompilerVersion[0] == '1' {
cairoProgram, err := starknet.StarknetProgramFromFile(pathToFile)
if err != nil {
return fmt.Errorf("cannot load program: %w", err)
}
hints, err = core.GetCairoHints(cairoProgram)
if err != nil {
return fmt.Errorf("cannot get hints: %w", err)
}
} else {
hints, err = hintrunner.GetZeroHints(zeroProgram)
if err != nil {
return fmt.Errorf("cannot create hints: %w", err)
}
hints, err := hintrunner.GetZeroHints(zeroProgram)
if err != nil {
return fmt.Errorf("cannot create hints: %w", err)
}
program, err := runner.LoadCairoZeroProgram(zeroProgram)
if err != nil {
return fmt.Errorf("cannot load program: %w", err)
}
fmt.Println("Running....")
runner, err := runner.NewRunner(program, hints, proofmode, collectTrace, maxsteps, layoutName)
if err != nil {
return fmt.Errorf("cannot create runner: %w", err)
runnerMode := runner.ExecutionMode
if proofmode {
runnerMode = runner.ProofModeCairo0
}

// Run executes main(), RunEntryPoint is used to test contract_class-style entry points.
// In theory, calling RunEntryPoint with main's offset should behave identically,
// 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 {
return fmt.Errorf("runtime error: %w", err)
}
} else {
if err := runner.RunEntryPoint(entrypointOffset); err != nil {
return fmt.Errorf("runtime error (entrypoint=%d): %w", entrypointOffset, err)
}
return runVM(*program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode)
},
},
{
Name: "cairo-run",
Usage: "runs a cairo zero compiled file",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "proofmode",
Usage: "runs the cairo vm in proof mode",
Required: false,
Destination: &proofmode,
},
&cli.Uint64Flag{
Name: "maxsteps",
Usage: "limits the execution steps to 'maxsteps'",
DefaultText: "2**64 - 1",
Value: math.MaxUint64,
Required: false,
Destination: &maxsteps,
},
&cli.Uint64Flag{
Name: "entrypoint",
Usage: "a PC offset that will be used as an entry point (by default it executes a main function)",
Value: 0,
Destination: &entrypointOffset,
},
&cli.BoolFlag{
Name: "collect_trace",
Usage: "collects the trace and builds the relocated trace after execution",
Required: false,
Destination: &collectTrace,
},
&cli.StringFlag{
Name: "tracefile",
Usage: "location to store the relocated trace",
Required: false,
Destination: &traceLocation,
},
&cli.BoolFlag{
Name: "build_memory",
Usage: "builds the relocated memory after execution",
Required: false,
Destination: &buildMemory,
},
&cli.StringFlag{
Name: "memoryfile",
Usage: "location to store the relocated memory",
Required: false,
Destination: &memoryLocation,
},
&cli.StringFlag{
Name: "layout",
Usage: "specifies the set of builtins to be used",
Required: false,
Destination: &layoutName,
},
&cli.StringFlag{
Name: "air_public_input",
Usage: "location to store the air_public_input",
Required: false,
Destination: &airPublicInputLocation,
},
&cli.StringFlag{
Name: "air_private_input",
Usage: "location to store the air_private_input",
Required: false,
Destination: &airPrivateInputLocation,
},
},
Action: func(ctx *cli.Context) error {
pathToFile := ctx.Args().Get(0)
if pathToFile == "" {
return fmt.Errorf("path to cairo file not set")
}

if proofmode {
if err := runner.EndRun(); err != nil {
return fmt.Errorf("cannot end run: %w", err)
}
if err := runner.FinalizeSegments(); err != nil {
return fmt.Errorf("cannot finalize segments: %w", err)
}
cairoProgram, err := starknet.StarknetProgramFromFile(pathToFile)
if err != nil {
return fmt.Errorf("cannot load program: %w", err)
}

if proofmode || collectTrace {
trace, err := runner.BuildTrace()
if err != nil {
return fmt.Errorf("cannot build trace: %w", err)
}

if traceLocation != "" {
if err := os.WriteFile(traceLocation, trace, 0644); err != nil {
return fmt.Errorf("cannot write relocated trace: %w", err)
}
}
hints, err := core.GetCairoHints(cairoProgram)
if err != nil {
return fmt.Errorf("cannot get hints: %w", err)
}

if proofmode || buildMemory {
memory, err := runner.BuildMemory()
if err != nil {
return fmt.Errorf("cannot build memory: %w", err)
}

if memoryLocation != "" {
if err := os.WriteFile(memoryLocation, memory, 0644); err != nil {
return fmt.Errorf("cannot write relocated memory: %w", err)
}
}
program, err := runner.LoadCairoProgram(cairoProgram)
if err != nil {
return fmt.Errorf("cannot load program: %w", err)
}

if proofmode {
if airPublicInputLocation != "" {
airPublicInput, err := runner.GetAirPublicInput()
if err != nil {
return err
}
airPublicInputJson, err := json.MarshalIndent(airPublicInput, "", " ")
if err != nil {
return err
}
err = os.WriteFile(airPublicInputLocation, airPublicInputJson, 0644)
if err != nil {
return fmt.Errorf("cannot write air_public_input: %w", err)
}
}

if airPrivateInputLocation != "" {
tracePath, err := filepath.Abs(traceLocation)
if err != nil {
return err
}
memoryPath, err := filepath.Abs(memoryLocation)
if err != nil {
return err
}
airPrivateInput, err := runner.GetAirPrivateInput(tracePath, memoryPath)
if err != nil {
return err
}
airPrivateInputJson, err := json.MarshalIndent(airPrivateInput, "", " ")
if err != nil {
return err
}
err = os.WriteFile(airPrivateInputLocation, airPrivateInputJson, 0644)
if err != nil {
return fmt.Errorf("cannot write air_private_input: %w", err)
}
}
entryCodeInstructions, err := runner.GetEntryCodeInstructions()
if err != nil {
return fmt.Errorf("cannot load entry code instructions: %w", err)
}

fmt.Println("Success!")
output := runner.Output()
if len(output) > 0 {
fmt.Println("Program output:")
for _, val := range output {
// cairo-run v0.11-0.13 pad the output lines with two spaces.
fmt.Printf(" %s\n", val)
}
program.Bytecode = append(entryCodeInstructions, program.Bytecode...)
program.Bytecode = append(program.Bytecode, runner.GetFooterInstructions()...)
runnerMode := runner.ExecutionMode
if proofmode {
runnerMode = runner.ProofModeCairo1
}
return nil
return runVM(*program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode)
},
},
},
Expand All @@ -250,3 +232,124 @@ func main() {
os.Exit(1)
}
}

func runVM(
program runner.Program,
proofmode bool,
maxsteps uint64,
entrypointOffset uint64,
collectTrace bool,
traceLocation string,
buildMemory bool,
memoryLocation string,
layoutName string,
airPublicInputLocation string,
airPrivateInputLocation string,
hints map[uint64][]hinter.Hinter,
runnerMode runner.RunnerMode,
) error {
fmt.Println("Running....")
runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName)
if err != nil {
return fmt.Errorf("cannot create runner: %w", err)
}

// Run executes main(), RunEntryPoint is used to test contract_class-style entry points.
// In theory, calling RunEntryPoint with main's offset should behave identically,
// 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 {
return fmt.Errorf("runtime error: %w", err)
}
} else {
if err := runner.RunEntryPoint(entrypointOffset); err != nil {
return fmt.Errorf("runtime error (entrypoint=%d): %w", entrypointOffset, err)
}
}
if proofmode {
if err := runner.EndRun(); err != nil {
return fmt.Errorf("cannot end run: %w", err)
}
if err := runner.FinalizeSegments(); err != nil {
return fmt.Errorf("cannot finalize segments: %w", err)
}
}

if proofmode || collectTrace {
trace, err := runner.BuildTrace()
if err != nil {
return fmt.Errorf("cannot build trace: %w", err)
}

if traceLocation != "" {
if err := os.WriteFile(traceLocation, trace, 0644); err != nil {
return fmt.Errorf("cannot write relocated trace: %w", err)
}
}
}

if proofmode || buildMemory {
memory, err := runner.BuildMemory()
if err != nil {
return fmt.Errorf("cannot build memory: %w", err)
}

if memoryLocation != "" {
if err := os.WriteFile(memoryLocation, memory, 0644); err != nil {
return fmt.Errorf("cannot write relocated memory: %w", err)
}
}
}

if proofmode {
if airPublicInputLocation != "" {
airPublicInput, err := runner.GetAirPublicInput()
if err != nil {
return err
}
airPublicInputJson, err := json.MarshalIndent(airPublicInput, "", " ")
if err != nil {
return err
}
err = os.WriteFile(airPublicInputLocation, airPublicInputJson, 0644)
if err != nil {
return fmt.Errorf("cannot write air_public_input: %w", err)
}
}

if airPrivateInputLocation != "" {
tracePath, err := filepath.Abs(traceLocation)
if err != nil {
return err
}
memoryPath, err := filepath.Abs(memoryLocation)
if err != nil {
return err
}
airPrivateInput, err := runner.GetAirPrivateInput(tracePath, memoryPath)
if err != nil {
return err
}
airPrivateInputJson, err := json.MarshalIndent(airPrivateInput, "", " ")
if err != nil {
return err
}
err = os.WriteFile(airPrivateInputLocation, airPrivateInputJson, 0644)
if err != nil {
return fmt.Errorf("cannot write air_private_input: %w", err)
}
}
}

fmt.Println("Success!")
output := runner.Output()
if len(output) > 0 {
fmt.Println("Program output:")
for _, val := range output {
// cairo-run v0.11-0.13 pad the output lines with two spaces.
fmt.Printf(" %s\n", val)
}
}
return nil
}
8 changes: 4 additions & 4 deletions pkg/assembler/assembler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ var parser *participle.Parser[CasmProgram] = participle.MustBuild[CasmProgram](
participle.UseLookahead(8),
)

// Given a CASM program it returns its encoded bytecode
func CasmToBytecode(code string) ([]*f.Element, error) {
// Given a CASM program it returns its encoded bytecode and a total size of processed instructions
func CasmToBytecode(code string) ([]*f.Element, uint8, error) {
casmAst, err := parser.ParseString("", code)
if err != nil {
return nil, err
return nil, 0, err
}
// Ast To Instruction List
wordList, err := astToInstruction(casmAst)
if err != nil {
return nil, err
return nil, 0, err
}
// Instruction to bytecode
return encodeInstructionListToBytecode(wordList)
Expand Down
Loading

0 comments on commit 212c55b

Please sign in to comment.