Repository: NethermindEth/cairo-vm-go
Pull request #648 (2024-08-29 18:25:38)

Add cairo1 runner

TAdev0: @MaksymMalicki  how is it going with this?
I see you made modification to the zero.go file, whats your plan exactly?
sweep-ai[bot]: Hey @MaksymMalicki, here is an example of how you can ask me to improve this pull request:


<blockquote>
@sweep Add unit tests for the `GetEntryCodeInstructions` function in `pkg/runner/runner.go` to verify the generated instructions for different parameter type combinations and ensure the correct offsets and builtin handling.
</blockquote>

:book: For more information on how to use Sweep, please read our [documentation](https://docs.sweep.dev).

<!--- MESSAGE_MARKER auto_suggestions -->

Patch:
diff --git a/cmd/cli/main.go b/cmd/cli/main.go
index 2fbad30..dd9f6d6 100644
--- a/cmd/cli/main.go
+++ b/cmd/cli/main.go
@@ -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")
@@ -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)
 				},
 			},
 		},
@@ -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
+}
diff --git a/pkg/assembler/assembler.go b/pkg/assembler/assembler.go
index 5954b9d..ff8aee9 100644
--- a/pkg/assembler/assembler.go
+++ b/pkg/assembler/assembler.go
@@ -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)
diff --git a/pkg/assembler/instruction.go b/pkg/assembler/instruction.go
index 51f1a3a..7c79d81 100644
--- a/pkg/assembler/instruction.go
+++ b/pkg/assembler/instruction.go
@@ -359,31 +359,32 @@ func decodeInstructionFlags(instruction *Instruction, flags uint16) error {
 /*
 *    Instruction list into bytecode functions
  */
-func encodeInstructionListToBytecode(wordList []Word) ([]*f.Element, error) {
+func encodeInstructionListToBytecode(wordList []Word) ([]*f.Element, uint8, error) {
 	n := len(wordList)
 	bytecodes := make([]*f.Element, 0, n+(n/2)+1)
-
+	var total_size uint8
 	for i, word := range wordList {
 		switch w := word.(type) {
 		case Instruction:
+			total_size += w.Size()
 			bytecode, err := encodeOneInstruction(&w)
 			if err != nil {
-				return nil, err
+				return nil, 0, err
 			}
 			bytecodes = append(bytecodes, bytecode)
 
 		case Immediate:
 			imm, err := new(f.Element).SetString(w)
 			if err != nil {
-				return nil, err
+				return nil, 0, err
 			}
 			bytecodes = append(bytecodes, imm)
 
 		default:
-			return nil, fmt.Errorf("word %d is not an instruction or immediate", i)
+			return nil, 0, fmt.Errorf("word %d is not an instruction or immediate", i)
 		}
 	}
-	return bytecodes, nil
+	return bytecodes, total_size, nil
 }
 
 // break the instruction into 4 segments of 16 bits
diff --git a/pkg/parsers/starknet/starknet.go b/pkg/parsers/starknet/starknet.go
index e61a7a6..0bbc615 100644
--- a/pkg/parsers/starknet/starknet.go
+++ b/pkg/parsers/starknet/starknet.go
@@ -9,16 +9,29 @@ import (
 	"github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
 )
 
-type EntryPointInfo struct {
+type EntryPointByTypeInfo struct {
 	Selector fp.Element             `json:"selector"`
 	Offset   fp.Element             `json:"offset"`
 	Builtins []builtins.BuiltinType `json:"builtins"`
 }
 
 type EntryPointByType struct {
-	External    []EntryPointInfo `json:"EXTERNAL"`
-	L1Handler   []EntryPointInfo `json:"L1_HANDLER"`
-	Constructor []EntryPointInfo `json:"CONSTRUCTOR"`
+	External    []EntryPointByTypeInfo `json:"EXTERNAL"`
+	L1Handler   []EntryPointByTypeInfo `json:"L1_HANDLER"`
+	Constructor []EntryPointByTypeInfo `json:"CONSTRUCTOR"`
+}
+
+type Arg struct {
+	GenericID string `json:"generic_id"`
+	Size      int    `json:"size"`
+	DebugName string `json:"debug_name"`
+}
+
+type EntryPointByFunction struct {
+	Offset    int                    `json:"offset"`
+	Builtins  []builtins.BuiltinType `json:"builtins"`
+	InputArgs []Arg                  `json:"input_args"`
+	ReturnArg []Arg                  `json:"return_arg"`
 }
 
 type Hints struct {
@@ -63,10 +76,11 @@ func (hints *Hints) MarshalJSON() ([]byte, error) {
 
 type StarknetProgram struct {
 	// Prime is fixed to be 0x800000000000011000000000000000000000000000000000000000000000001 and wont fit in a f.Felt
-	Bytecode        []fp.Element     `json:"bytecode"`
-	CompilerVersion string           `json:"compiler_version"`
-	EntryPoints     EntryPointByType `json:"entry_points_by_type"`
-	Hints           []Hints          `json:"hints" validate:"required"`
+	Bytecode              []fp.Element                    `json:"bytecode"`
+	CompilerVersion       string                          `json:"compiler_version"`
+	EntryPointsByType     EntryPointByType                `json:"entry_points_by_type"`
+	EntryPointsByFunction map[string]EntryPointByFunction `json:"entry_points_by_function"`
+	Hints                 []Hints                         `json:"hints" validate:"required"`
 }
 
 func StarknetProgramFromFile(pathToFile string) (*StarknetProgram, error) {
diff --git a/pkg/runner/air_input.go b/pkg/runner/air_input.go
index 8a7038e..ba96df8 100644
--- a/pkg/runner/air_input.go
+++ b/pkg/runner/air_input.go
@@ -5,7 +5,7 @@ import (
 	"github.com/NethermindEth/cairo-vm-go/pkg/vm/builtins"
 )
 
-func (runner *ZeroRunner) GetAirPublicInput() (AirPublicInput, error) {
+func (runner *Runner) GetAirPublicInput() (AirPublicInput, error) {
 	rcMin, rcMax := runner.getPermRangeCheckLimits()
 
 	// TODO: refactor to reuse earlier computed relocated trace
@@ -52,7 +52,7 @@ type AirPublicMemoryEntry struct {
 	Page    uint16 `json:"page"`
 }
 
-func (runner *ZeroRunner) GetAirPrivateInput(tracePath, memoryPath string) (AirPrivateInput, error) {
+func (runner *Runner) GetAirPrivateInput(tracePath, memoryPath string) (AirPrivateInput, error) {
 	airPrivateInput := AirPrivateInput{
 		TracePath:  tracePath,
 		MemoryPath: memoryPath,
diff --git a/pkg/runner/program.go b/pkg/runner/program.go
index adc30a7..d0920b5 100644
--- a/pkg/runner/program.go
+++ b/pkg/runner/program.go
@@ -3,12 +3,13 @@ package runner
 import (
 	"fmt"
 
+	sn "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet"
 	"github.com/NethermindEth/cairo-vm-go/pkg/parsers/zero"
 	"github.com/NethermindEth/cairo-vm-go/pkg/vm/builtins"
 	"github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
 )
 
-type ZeroProgram struct {
+type Program struct {
 	// the bytecode in string format
 	Bytecode []*fp.Element
 	// given a string it returns the pc for that function call
@@ -21,7 +22,7 @@ type ZeroProgram struct {
 
 type CairoProgram struct{}
 
-func LoadCairoZeroProgram(cairoZeroJson *zero.ZeroProgram) (*ZeroProgram, error) {
+func LoadCairoZeroProgram(cairoZeroJson *zero.ZeroProgram) (*Program, error) {
 	// bytecode
 	bytecode := make([]*fp.Element, len(cairoZeroJson.Data))
 	for i := range cairoZeroJson.Data {
@@ -37,7 +38,7 @@ func LoadCairoZeroProgram(cairoZeroJson *zero.ZeroProgram) (*ZeroProgram, error)
 
 	entrypoints, labels := extractEntrypointsAndLabels(cairoZeroJson)
 
-	return &ZeroProgram{
+	return &Program{
 		Bytecode:    bytecode,
 		Entrypoints: entrypoints,
 		Labels:      labels,
@@ -64,3 +65,43 @@ func extractEntrypointsAndLabels(json *zero.ZeroProgram) (map[string]uint64, map
 
 	return entrypoints, labels
 }
+
+func LoadCairoProgram(cairoProgram *sn.StarknetProgram) (*Program, error) {
+	bytecode := make([]*fp.Element, len(cairoProgram.Bytecode))
+	for i, felt := range cairoProgram.Bytecode {
+		f := felt
+		bytecode[i] = &f
+	}
+	entrypoints := extractCairoEntryPoints(cairoProgram)
+	builtins := extractCairoBuiltins(cairoProgram)
+
+	return &Program{
+		Bytecode:    bytecode,
+		Entrypoints: entrypoints,
+		Labels:      nil,
+		Builtins:    builtins,
+	}, nil
+}
+
+func extractCairoEntryPoints(cairoProgram *sn.StarknetProgram) map[string]uint64 {
+	entrypoints := make(map[string]uint64)
+
+	for name, entry := range cairoProgram.EntryPointsByFunction {
+		entrypoints[name] = uint64(entry.Offset)
+	}
+	return entrypoints
+}
+
+func extractCairoBuiltins(cairoProgram *sn.StarknetProgram) []builtins.BuiltinType {
+	uniqueBuiltins := make(map[builtins.BuiltinType]struct{})
+	var builtins []builtins.BuiltinType
+	for _, entry := range cairoProgram.EntryPointsByFunction {
+		for _, builtin := range entry.Builtins {
+			if _, exists := uniqueBuiltins[builtin]; !exists {
+				uniqueBuiltins[builtin] = struct{}{}
+				builtins = append(builtins, builtin)
+			}
+		}
+	}
+	return builtins
+}
diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go
index bff732b..66db1cb 100644
--- a/pkg/runner/runner.go
+++ b/pkg/runner/runner.go
@@ -4,6 +4,7 @@ import (
 	"errors"
 	"fmt"
 
+	"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/utils"
@@ -13,15 +14,23 @@ import (
 	"github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
 )
 
-type ZeroRunner struct {
+type RunnerMode uint8
+
+const (
+	ExecutionMode RunnerMode = iota + 1
+	ProofModeCairo0
+	ProofModeCairo1
+)
+
+type Runner struct {
 	// core components
-	program    *ZeroProgram
+	program    *Program
 	vm         *vm.VirtualMachine
 	hintrunner hintrunner.HintRunner
 	// config
-	proofmode    bool
 	collectTrace bool
 	maxsteps     uint64
+	runnerMode   RunnerMode
 	// auxiliar
 	runFinished bool
 	layout      builtins.Layout
@@ -30,16 +39,16 @@ type ZeroRunner struct {
 type CairoRunner struct{}
 
 // Creates a new Runner of a Cairo Zero program
-func NewRunner(program *ZeroProgram, hints map[uint64][]hinter.Hinter, proofmode bool, collectTrace bool, maxsteps uint64, layoutName string) (ZeroRunner, error) {
+func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode RunnerMode, collectTrace bool, maxsteps uint64, layoutName string) (Runner, error) {
 	hintrunner := hintrunner.NewHintRunner(hints)
 	layout, err := builtins.GetLayout(layoutName)
 	if err != nil {
-		return ZeroRunner{}, err
+		return Runner{}, err
 	}
-	return ZeroRunner{
+	return Runner{
 		program:      program,
+		runnerMode:   runnerMode,
 		hintrunner:   hintrunner,
-		proofmode:    proofmode,
 		collectTrace: collectTrace,
 		maxsteps:     maxsteps,
 		layout:       layout,
@@ -48,7 +57,7 @@ func NewRunner(program *ZeroProgram, hints map[uint64][]hinter.Hinter, proofmode
 
 // 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 *ZeroRunner) RunEntryPoint(pc uint64) error {
+func (runner *Runner) RunEntryPoint(pc uint64) error {
 	if runner.runFinished {
 		return errors.New("cannot re-run using the same runner")
 	}
@@ -67,7 +76,11 @@ func (runner *ZeroRunner) RunEntryPoint(pc uint64) error {
 
 	returnFp := memory.AllocateEmptySegment()
 	mvReturnFp := mem.MemoryValueFromMemoryAddress(&returnFp)
-	end, err := runner.initializeEntrypoint(pc, nil, &mvReturnFp, memory, stack)
+	cairo1FpOffset := uint64(0)
+	if runner.runnerMode == ProofModeCairo1 {
+		cairo1FpOffset = 2
+	}
+	end, err := runner.initializeEntrypoint(pc, nil, &mvReturnFp, memory, stack, cairo1FpOffset)
 	if err != nil {
 		return err
 	}
@@ -79,7 +92,7 @@ func (runner *ZeroRunner) RunEntryPoint(pc uint64) error {
 	return nil
 }
 
-func (runner *ZeroRunner) Run() error {
+func (runner *Runner) Run() error {
 	if runner.runFinished {
 		return errors.New("cannot re-run using the same runner")
 	}
@@ -94,7 +107,7 @@ func (runner *ZeroRunner) Run() error {
 		return err
 	}
 
-	if runner.proofmode {
+	if runner.runnerMode == ProofModeCairo0 || runner.runnerMode == ProofModeCairo1 {
 		// +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)
@@ -105,7 +118,7 @@ func (runner *ZeroRunner) Run() error {
 	return nil
 }
 
-func (runner *ZeroRunner) initializeSegments() (*mem.Memory, error) {
+func (runner *Runner) initializeSegments() (*mem.Memory, error) {
 	memory := mem.InitializeEmptyMemory()
 	_, err := memory.AllocateSegment(runner.program.Bytecode) // ProgramSegment
 	if err != nil {
@@ -116,7 +129,7 @@ func (runner *ZeroRunner) initializeSegments() (*mem.Memory, error) {
 	return memory, nil
 }
 
-func (runner *ZeroRunner) initializeMainEntrypoint() (mem.MemoryAddress, error) {
+func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) {
 	memory, err := runner.initializeSegments()
 	if err != nil {
 		return mem.UnknownAddress, err
@@ -126,8 +139,20 @@ func (runner *ZeroRunner) initializeMainEntrypoint() (mem.MemoryAddress, error)
 	if err != nil {
 		return mem.UnknownAddress, err
 	}
-
-	if runner.proofmode {
+	switch runner.runnerMode {
+	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")
+		}
+		if runner.runnerMode == ExecutionMode {
+			return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0)
+		} else {
+			return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 2)
+		}
+	case ProofModeCairo0:
 		initialPCOffset, ok := runner.program.Labels["__start__"]
 		if !ok {
 			return mem.UnknownAddress,
@@ -148,7 +173,7 @@ func (runner *ZeroRunner) initializeMainEntrypoint() (mem.MemoryAddress, error)
 		if err := runner.initializeVm(&mem.MemoryAddress{
 			SegmentIndex: vm.ProgramSegment,
 			Offset:       initialPCOffset,
-		}, stack, memory); err != nil {
+		}, stack, memory, 0); err != nil {
 			return mem.UnknownAddress, err
 		}
 
@@ -156,33 +181,26 @@ func (runner *ZeroRunner) initializeMainEntrypoint() (mem.MemoryAddress, error)
 		runner.vm.Context.Ap = 2
 		runner.vm.Context.Fp = 2
 		return mem.MemoryAddress{SegmentIndex: vm.ProgramSegment, Offset: endPcOffset}, nil
-	}
 
-	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")
 	}
-	return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack)
+	return mem.UnknownAddress, errors.New("unknown runner mode")
 }
 
-func (runner *ZeroRunner) initializeEntrypoint(
-	initialPCOffset uint64, arguments []*fp.Element, returnFp *mem.MemoryValue, memory *mem.Memory, stack []mem.MemoryValue,
+func (runner *Runner) initializeEntrypoint(
+	initialPCOffset uint64, arguments []*fp.Element, returnFp *mem.MemoryValue, memory *mem.Memory, stack []mem.MemoryValue, cairo1FpOffset uint64,
 ) (mem.MemoryAddress, error) {
 	for i := range arguments {
 		stack = append(stack, mem.MemoryValueFromFieldElement(arguments[i]))
 	}
-	end := memory.AllocateEmptySegment()
-
-	stack = append(stack, *returnFp, mem.MemoryValueFromMemoryAddress(&end))
-	return end, runner.initializeVm(&mem.MemoryAddress{
+	endPC := memory.AllocateEmptySegment()
+	stack = append(stack, *returnFp, mem.MemoryValueFromMemoryAddress(&endPC))
+	return endPC, runner.initializeVm(&mem.MemoryAddress{
 		SegmentIndex: vm.ProgramSegment,
 		Offset:       initialPCOffset,
-	}, stack, memory)
+	}, stack, memory, cairo1FpOffset)
 }
 
-func (runner *ZeroRunner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, error) {
+func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, error) {
 	builtinsSet := make(map[builtins.BuiltinType]bool)
 	for _, bRunner := range runner.layout.Builtins {
 		builtinsSet[bRunner.Builtin] = true
@@ -208,8 +226,8 @@ func (runner *ZeroRunner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryVa
 	return stack, nil
 }
 
-func (runner *ZeroRunner) initializeVm(
-	initialPC *mem.MemoryAddress, stack []mem.MemoryValue, memory *mem.Memory,
+func (runner *Runner) initializeVm(
+	initialPC *mem.MemoryAddress, stack []mem.MemoryValue, memory *mem.Memory, cairo1FpOffset uint64,
 ) error {
 	executionSegment := memory.Segments[vm.ExecutionSegment]
 	offset := executionSegment.Len()
@@ -220,18 +238,22 @@ func (runner *ZeroRunner) initializeVm(
 		}
 	}
 
+	initialFp := offset + uint64(len(stack)) + cairo1FpOffset
 	var err error
 	// initialize vm
 	runner.vm, err = vm.NewVirtualMachine(vm.Context{
 		Pc: *initialPC,
-		Ap: offset + stackSize,
-		Fp: offset + stackSize,
-	}, memory, vm.VirtualMachineConfig{ProofMode: runner.proofmode, CollectTrace: runner.collectTrace})
+		Ap: initialFp,
+		Fp: initialFp,
+	}, memory, vm.VirtualMachineConfig{
+		ProofMode:    runner.runnerMode == ProofModeCairo0 || runner.runnerMode == ProofModeCairo1,
+		CollectTrace: runner.collectTrace,
+	})
 	return err
 }
 
 // run until the program counter equals the `pc` parameter
-func (runner *ZeroRunner) RunUntilPc(pc *mem.MemoryAddress) error {
+func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error {
 	for !runner.vm.Context.Pc.Equal(pc) {
 		if runner.steps() >= runner.maxsteps {
 			return fmt.Errorf(
@@ -249,7 +271,7 @@ func (runner *ZeroRunner) RunUntilPc(pc *mem.MemoryAddress) error {
 }
 
 // run until the vm step count reaches the `steps` parameter
-func (runner *ZeroRunner) RunFor(steps uint64) error {
+func (runner *Runner) RunFor(steps uint64) error {
 	for runner.steps() < steps {
 		if runner.steps() >= runner.maxsteps {
 			return fmt.Errorf(
@@ -275,7 +297,7 @@ func (runner *ZeroRunner) RunFor(steps uint64) error {
 // until the checkUsedCells doesn't return any error.
 // Since this vm always finishes the run of the program at the number of steps that is a power of two in the proof mode,
 // there is no need to run additional steps before the loop.
-func (runner *ZeroRunner) EndRun() error {
+func (runner *Runner) EndRun() error {
 	for runner.checkUsedCells() != nil {
 		pow2Steps := utils.NextPowerOfTwo(runner.vm.Step + 1)
 		if err := runner.RunFor(pow2Steps); err != nil {
@@ -287,7 +309,7 @@ func (runner *ZeroRunner) EndRun() error {
 
 // checkUsedCells returns error if not enough steps were made to allocate required number of cells for builtins
 // or there are not enough trace cells to fill the entire range check range
-func (runner *ZeroRunner) checkUsedCells() error {
+func (runner *Runner) checkUsedCells() error {
 	for _, bRunner := range runner.layout.Builtins {
 		builtinName := bRunner.Runner.String()
 		builtinSegment, ok := runner.vm.Memory.FindSegmentWithBuiltin(builtinName)
@@ -306,7 +328,7 @@ func (runner *ZeroRunner) checkUsedCells() error {
 }
 
 // Checks if there are not enough trace cells to fill the entire range check range. Each step has assigned a number of range check units. If the number of unused range check units is less than the range of potential values to be checked (defined by rcMin and rcMax), the number of trace cells must be increased, by running additional steps.
-func (runner *ZeroRunner) checkRangeCheckUsage() error {
+func (runner *Runner) checkRangeCheckUsage() error {
 	rcMin, rcMax := runner.getPermRangeCheckLimits()
 	var rcUnitsUsedByBuiltins uint64
 	for _, builtin := range runner.program.Builtins {
@@ -335,7 +357,7 @@ func (runner *ZeroRunner) checkRangeCheckUsage() error {
 }
 
 // getPermRangeCheckLimits returns the minimum and maximum values used by the range check units in the program. To find the values, maximum and minimum values from the range check segment are compared with maximum and minimum values of instructions offsets calculated during running the instructions.
-func (runner *ZeroRunner) getPermRangeCheckLimits() (uint16, uint16) {
+func (runner *Runner) getPermRangeCheckLimits() (uint16, uint16) {
 	rcMin, rcMax := runner.vm.RcLimitsMin, runner.vm.RcLimitsMax
 
 	for _, builtin := range runner.program.Builtins {
@@ -360,7 +382,7 @@ func (runner *ZeroRunner) getPermRangeCheckLimits() (uint16, uint16) {
 // FinalizeSegments calculates the final size of the builtins segments,
 // using number of allocated instances and memory cells per builtin instance.
 // Additionally it sets the final size of the program segment to the program size.
-func (runner *ZeroRunner) FinalizeSegments() error {
+func (runner *Runner) FinalizeSegments() error {
 	programSize := uint64(len(runner.program.Bytecode))
 	runner.vm.Memory.Segments[vm.ProgramSegment].Finalize(programSize)
 	for _, bRunner := range runner.layout.Builtins {
@@ -377,29 +399,29 @@ func (runner *ZeroRunner) FinalizeSegments() error {
 }
 
 // BuildMemory relocates the memory and returns it
-func (runner *ZeroRunner) BuildMemory() ([]byte, error) {
+func (runner *Runner) BuildMemory() ([]byte, error) {
 	relocatedMemory := runner.vm.RelocateMemory()
 	return vm.EncodeMemory(relocatedMemory), nil
 }
 
 // BuildTrace relocates the trace and returns it
-func (runner *ZeroRunner) BuildTrace() ([]byte, error) {
+func (runner *Runner) BuildTrace() ([]byte, error) {
 	relocatedTrace := make([]vm.Trace, len(runner.vm.Trace))
 	runner.vm.RelocateTrace(&relocatedTrace)
 	return vm.EncodeTrace(relocatedTrace), nil
 }
 
-func (runner *ZeroRunner) pc() mem.MemoryAddress {
+func (runner *Runner) pc() mem.MemoryAddress {
 	return runner.vm.Context.Pc
 }
 
-func (runner *ZeroRunner) steps() uint64 {
+func (runner *Runner) steps() uint64 {
 	return runner.vm.Step
 }
 
 // Gives the output of the last run. Panics if there hasn't
 // been any runs yet.
-func (runner *ZeroRunner) Output() []*fp.Element {
+func (runner *Runner) Output() []*fp.Element {
 	if runner.vm == nil {
 		panic("cannot get the output from an uninitialized runner")
 	}
@@ -419,3 +441,124 @@ func (runner *ZeroRunner) Output() []*fp.Element {
 	}
 	return output
 }
+
+type InlineCasmContext struct {
+	instructions      []*fp.Element
+	currentCodeOffset int
+}
+
+func (ctx *InlineCasmContext) AddInlineCASM(code string) {
+	bytecode, total_size, err := assembler.CasmToBytecode(code)
+	if err != nil {
+		panic(err)
+	}
+	ctx.instructions = append(ctx.instructions, bytecode...)
+	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
+	}{}
+	codeOffset := 0
+
+	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,
+	}
+
+	emulatedBuiltins := map[builtins.BuiltinType]struct{}{
+		1: {},
+	}
+
+	apOffset := 0
+	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
+			}
+		}
+	}
+	ctx.AddInlineCASM(
+		fmt.Sprintf("ap += %d;", paramsSize),
+	)
+	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`,
+			)
+		}
+	}
+
+	usedArgs := 0
+	for _, param := range paramTypes {
+		ty, tySize := param.genericTypeId, param.size
+		if offset, isBuiltin := builtinOffset[ty]; isBuiltin {
+			ctx.AddInlineCASM(
+				fmt.Sprintf("[ap + 0] = [fp - %d], ap++;", offset),
+			)
+			apOffset += 1
+		} else if _, emulated := emulatedBuiltins[ty]; emulated {
+			ctx.AddInlineCASM(
+				`memory[ap + 0] = segments.add();
+				ap += 1;`,
+			)
+			apOffset += 1
+		} else if ty == 99 {
+			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
+			}
+		}
+	}
+
+	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")
+	}
+
+	return ctx.instructions, nil
+}
+
+func GetFooterInstructions() []*fp.Element {
+	// Add a `ret` instruction used in libfuncs that retrieve the current value of the `fp`
+	// and `pc` registers.
+	return []*fp.Element{new(fp.Element).SetUint64(2345108766317314046)}
+}
diff --git a/pkg/vm/utils.go b/pkg/vm/utils.go
index 97ec924..5a64374 100644
--- a/pkg/vm/utils.go
+++ b/pkg/vm/utils.go
@@ -12,7 +12,7 @@ func DefaultVirtualMachine() *VirtualMachine {
 }
 
 func defaultVirtualMachineWithCode(code string) *VirtualMachine {
-	bytecode, err := a.CasmToBytecode(code)
+	bytecode, _, err := a.CasmToBytecode(code)
 	if err != nil {
 		panic(err)
 	}


Test patch:
diff --git a/pkg/assembler/assembler_test.go b/pkg/assembler/assembler_test.go
index 188361e..065a376 100644
--- a/pkg/assembler/assembler_test.go
+++ b/pkg/assembler/assembler_test.go
@@ -625,7 +625,7 @@ func TestCallAbsNegative(t *testing.T) {
 }
 
 func parseImmediateInstruction(casmCode string) (uint64, *f.Element) {
-	instructions, err := CasmToBytecode(casmCode)
+	instructions, _, err := CasmToBytecode(casmCode)
 	if err != nil {
 		panic(err)
 	}
@@ -638,7 +638,7 @@ func parseImmediateInstruction(casmCode string) (uint64, *f.Element) {
 }
 
 func parseSingleInstruction(casmCode string) uint64 {
-	instructions, err := CasmToBytecode(casmCode)
+	instructions, _, err := CasmToBytecode(casmCode)
 	if err != nil {
 		panic(err)
 	}
diff --git a/pkg/disasm/disasm_test.go b/pkg/disasm/disasm_test.go
index 2330b84..ee51b76 100644
--- a/pkg/disasm/disasm_test.go
+++ b/pkg/disasm/disasm_test.go
@@ -64,7 +64,7 @@ func TestDisasm(t *testing.T) {
 			if err != nil {
 				t.Fatal(err)
 			}
-			instList, err := assembler.CasmToBytecode(string(testCode))
+			instList, _, err := assembler.CasmToBytecode(string(testCode))
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -76,7 +76,7 @@ func TestDisasm(t *testing.T) {
 				t.Fatal(err)
 			}
 			testCode2 := disasmProgToString(disassembled)
-			instList2, err := assembler.CasmToBytecode(testCode2)
+			instList2, _, err := assembler.CasmToBytecode(testCode2)
 			if err != nil {
 				t.Fatalf("generated casm parse error: %v\nprog:\n%s", err, testCode2)
 			}
diff --git a/pkg/parsers/starknet/starknet_test.go b/pkg/parsers/starknet/starknet_test.go
index 9e696e0..64842bc 100644
--- a/pkg/parsers/starknet/starknet_test.go
+++ b/pkg/parsers/starknet/starknet_test.go
@@ -51,7 +51,7 @@ func TestEmptyEntryPointTypeParsing(t *testing.T) {
 	starknet, err := StarknetProgramFromJSON(testData)
 	require.NoError(t, err)
 
-	entryPoints := starknet.EntryPoints
+	entryPoints := starknet.EntryPointsByType
 	assert.Empty(t, entryPoints.External)
 	assert.Empty(t, entryPoints.L1Handler)
 	assert.Empty(t, entryPoints.Constructor)
@@ -86,7 +86,7 @@ func TestEntryPointInfoParsing(t *testing.T) {
 	starknet, err := StarknetProgramFromJSON(testData)
 	require.NoError(t, err)
 
-	entryPoints := starknet.EntryPoints
+	entryPoints := starknet.EntryPointsByType
 	assert.Len(t, entryPoints.External, 1)
 
 	entryPointInfo := entryPoints.External[0]
diff --git a/pkg/runner/program_test.go b/pkg/runner/program_test.go
index 599f8b1..ca98c99 100644
--- a/pkg/runner/program_test.go
+++ b/pkg/runner/program_test.go
@@ -50,7 +50,7 @@ func TestLoadCairoZeroProgram(t *testing.T) {
 	program, err := LoadCairoZeroProgram(cairoZeroJson)
 	require.NoError(t, err)
 
-	require.Equal(t, &ZeroProgram{
+	require.Equal(t, &Program{
 		Bytecode: []*fp.Element{
 			stringToFelt("0x01"),
 			stringToFelt("0x02"),
diff --git a/pkg/runner/runner_benchmark_test.go b/pkg/runner/runner_benchmark_test.go
index 71668ff..d86be97 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, true, false, math.MaxUint64, "plain")
+		runner, err := NewRunner(program, hints, ProofModeCairo0, false, math.MaxUint64, "plain")
 		if err != nil {
 			panic(err)
 		}
diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go
index abad43e..8c876d6 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, false, false, math.MaxUint64, "plain")
+	runner, err := NewRunner(program, hints, ExecutionMode, false, math.MaxUint64, "plain")
 	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, false, false, 3, "plain")
+	runner, err := NewRunner(program, hints, ExecutionMode, false, 3, "plain")
 	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, true, false, uint64(maxstep), "plain")
+		runner, err := NewRunner(program, hints, ProofModeCairo0, false, uint64(maxstep), "plain")
 		require.NoError(t, err)
 
 		err = runner.Run()
@@ -432,11 +432,11 @@ func TestModuloBuiltin(t *testing.T) {
 	// requireEqualSegments(t, createSegment(2048, 5), modulo)
 }
 
-func createRunner(code string, layoutName string, builtins ...builtins.BuiltinType) ZeroRunner {
+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, false, false, math.MaxUint64, layoutName)
+	runner, err := NewRunner(program, hints, ExecutionMode, false, math.MaxUint64, layoutName)
 	if err != nil {
 		panic(err)
 	}
@@ -482,13 +482,13 @@ func trimmedSegment(segment *memory.Segment) *memory.Segment {
 	return segment
 }
 
-func createProgram(code string) *ZeroProgram {
-	bytecode, err := assembler.CasmToBytecode(code)
+func createProgram(code string) *Program {
+	bytecode, _, err := assembler.CasmToBytecode(code)
 	if err != nil {
 		panic(err)
 	}
 
-	program := ZeroProgram{
+	program := Program{
 		Bytecode: bytecode,
 		Entrypoints: map[string]uint64{
 			"main": 0,
@@ -498,7 +498,7 @@ func createProgram(code string) *ZeroProgram {
 	return &program
 }
 
-func createProgramWithBuiltins(code string, builtins ...builtins.BuiltinType) *ZeroProgram {
+func createProgramWithBuiltins(code string, builtins ...builtins.BuiltinType) *Program {
 	program := createProgram(code)
 	program.Builtins = builtins
 	return program