From 78c0a0897967ad99aeda5c3354f6496dbf3dde58 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Fri, 29 Sep 2023 21:37:52 +0300 Subject: [PATCH] Add `ExecutionResources` + fix `CairoRunner` methods that received vm as an argument (#300) * Add ExecutionResources struct * Fix: Remove vm argument from CairoRunner methods * Finish func + remove todos * Add unit test * Add `RunUntilPc` & `GetReturnValues` (#302) * Implement GenArg * Remove recursive processing * Add unit tests * Fix test values * Start fn * Add RunFromEntryPoint * Add test for RunFromEntryPoint * Add unit tests * Add comments --------- Co-authored-by: Pedro Fontana --- pkg/runners/cairo_runner.go | 171 +++++++++++++++++++---------- pkg/runners/cairo_runner_test.go | 104 ++++++++++++------ pkg/runners/execution_resources.go | 7 ++ pkg/vm/cairo_run/cairo_run.go | 6 +- pkg/vm/memory/segments.go | 38 +++++++ pkg/vm/memory/segments_test.go | 49 +++++++++ pkg/vm/vm_core.go | 9 ++ pkg/vm/vm_test.go | 28 +++++ 8 files changed, 321 insertions(+), 91 deletions(-) create mode 100644 pkg/runners/execution_resources.go diff --git a/pkg/runners/cairo_runner.go b/pkg/runners/cairo_runner.go index e9baf5dc..32cb37d4 100644 --- a/pkg/runners/cairo_runner.go +++ b/pkg/runners/cairo_runner.go @@ -70,11 +70,11 @@ func NewCairoRunner(program vm.Program, layoutName string, proofMode bool) (*Cai // Performs the initialization step, returns the end pointer (pc upon which execution should stop) func (r *CairoRunner) Initialize() (memory.Relocatable, error) { - err := r.initializeBuiltins() + err := r.InitializeBuiltins() if err != nil { return memory.Relocatable{}, errors.New(err.Error()) } - r.initializeSegments() + r.InitializeSegments() end, err := r.initializeMainEntrypoint() if err == nil { err = r.initializeVM() @@ -84,7 +84,7 @@ func (r *CairoRunner) Initialize() (memory.Relocatable, error) { // Initializes builtin runners in accordance to the specified layout and // the builtins present in the running program. -func (r *CairoRunner) initializeBuiltins() error { +func (r *CairoRunner) InitializeBuiltins() error { var builtinRunners []builtins.BuiltinRunner programBuiltins := map[string]struct{}{} for _, builtin := range r.Program.Builtins { @@ -113,7 +113,7 @@ func (r *CairoRunner) initializeBuiltins() error { } // Creates program, execution and builtin segments -func (r *CairoRunner) initializeSegments() { +func (r *CairoRunner) InitializeSegments() { // Program Segment r.ProgramBase = r.Vm.Segments.AddSegment() // Execution Segment @@ -144,9 +144,9 @@ func (r *CairoRunner) initializeState(entrypoint uint, stack *[]memory.MaybeRelo // Initializes memory, initial register values & returns the end pointer (final pc) to run from a given pc offset // (entrypoint) -func (r *CairoRunner) initializeFunctionEntrypoint(entrypoint uint, stack *[]memory.MaybeRelocatable, return_fp memory.Relocatable) (memory.Relocatable, error) { +func (r *CairoRunner) initializeFunctionEntrypoint(entrypoint uint, stack *[]memory.MaybeRelocatable, return_fp memory.MaybeRelocatable) (memory.Relocatable, error) { end := r.Vm.Segments.AddSegment() - *stack = append(*stack, *memory.NewMaybeRelocatableRelocatable(return_fp), *memory.NewMaybeRelocatableRelocatable(end)) + *stack = append(*stack, return_fp, *memory.NewMaybeRelocatableRelocatable(end)) r.initialFp = r.executionBase r.initialFp.Offset += uint(len(*stack)) r.initialAp = r.initialFp @@ -188,7 +188,7 @@ func (r *CairoRunner) initializeMainEntrypoint() (memory.Relocatable, error) { return memory.NewRelocatable(r.ProgramBase.SegmentIndex, r.ProgramBase.Offset+r.Program.End), nil } - return_fp := r.Vm.Segments.AddSegment() + return_fp := *memory.NewMaybeRelocatableRelocatable(r.Vm.Segments.AddSegment()) return r.initializeFunctionEntrypoint(r.mainOffset, &stack, return_fp) } @@ -237,7 +237,7 @@ func (r *CairoRunner) RunUntilPC(end memory.Relocatable, hintProcessor vm.HintPr return nil } -func (runner *CairoRunner) EndRun(disableTracePadding bool, disableFinalizeAll bool, vm *vm.VirtualMachine, hintProcessor vm.HintProcessor) error { +func (runner *CairoRunner) EndRun(disableTracePadding bool, disableFinalizeAll bool, hintProcessor vm.HintProcessor) error { if runner.RunEnded { return ErrRunnerCalledTwice } @@ -245,7 +245,7 @@ func (runner *CairoRunner) EndRun(disableTracePadding bool, disableFinalizeAll b // TODO: This seems to have to do with temporary segments // vm.Segments.Memory.RelocateMemory() - err := vm.EndRun() + err := runner.Vm.EndRun() if err != nil { return err } @@ -254,15 +254,15 @@ func (runner *CairoRunner) EndRun(disableTracePadding bool, disableFinalizeAll b return nil } - vm.Segments.ComputeEffectiveSizes() + runner.Vm.Segments.ComputeEffectiveSizes() if runner.ProofMode && !disableTracePadding { - err := runner.RunUntilNextPowerOfTwo(vm, hintProcessor) + err := runner.RunUntilNextPowerOfTwo(hintProcessor) if err != nil { return err } for true { - err := runner.CheckUsedCells(vm) + err := runner.CheckUsedCells() if errors.Unwrap(err) == memory.ErrInsufficientAllocatedCells { } else if err != nil { return err @@ -270,12 +270,12 @@ func (runner *CairoRunner) EndRun(disableTracePadding bool, disableFinalizeAll b break } - err = runner.RunForSteps(1, vm, hintProcessor) + err = runner.RunForSteps(1, hintProcessor) if err != nil { return err } - err = runner.RunUntilNextPowerOfTwo(vm, hintProcessor) + err = runner.RunUntilNextPowerOfTwo(hintProcessor) if err != nil { return err } @@ -286,7 +286,7 @@ func (runner *CairoRunner) EndRun(disableTracePadding bool, disableFinalizeAll b return nil } -func (r *CairoRunner) FinalizeSegments(virtualMachine vm.VirtualMachine) error { +func (r *CairoRunner) FinalizeSegments() error { if r.SegmentsFinalized { return nil } @@ -304,7 +304,7 @@ func (r *CairoRunner) FinalizeSegments(virtualMachine vm.VirtualMachine) error { publicMemory = append(publicMemory, i) } - virtualMachine.Segments.Finalize(size, uint(r.ProgramBase.SegmentIndex), &publicMemory) + r.Vm.Segments.Finalize(size, uint(r.ProgramBase.SegmentIndex), &publicMemory) publicMemory = make([]uint, 0) execBase := r.executionBase @@ -316,9 +316,9 @@ func (r *CairoRunner) FinalizeSegments(virtualMachine vm.VirtualMachine) error { publicMemory = append(publicMemory, elem+execBase.Offset) } - virtualMachine.Segments.Finalize(nil, uint(execBase.SegmentIndex), &publicMemory) - for _, builtin := range virtualMachine.BuiltinRunners { - _, size, err := builtin.GetUsedCellsAndAllocatedSizes(&virtualMachine.Segments, virtualMachine.CurrentStep) + r.Vm.Segments.Finalize(nil, uint(execBase.SegmentIndex), &publicMemory) + for _, builtin := range r.Vm.BuiltinRunners { + _, size, err := builtin.GetUsedCellsAndAllocatedSizes(&r.Vm.Segments, r.Vm.CurrentStep) if err != nil { return err } @@ -329,9 +329,9 @@ func (r *CairoRunner) FinalizeSegments(virtualMachine vm.VirtualMachine) error { for i = 0; i < size; i++ { publicMemory = append(publicMemory, i) } - virtualMachine.Segments.Finalize(&size, uint(builtin.Base().SegmentIndex), &publicMemory) + r.Vm.Segments.Finalize(&size, uint(builtin.Base().SegmentIndex), &publicMemory) } else { - virtualMachine.Segments.Finalize(&size, uint(builtin.Base().SegmentIndex), nil) + r.Vm.Segments.Finalize(&size, uint(builtin.Base().SegmentIndex), nil) } } @@ -339,15 +339,15 @@ func (r *CairoRunner) FinalizeSegments(virtualMachine vm.VirtualMachine) error { return nil } -func (r *CairoRunner) ReadReturnValues(virtualMachine *vm.VirtualMachine) error { +func (r *CairoRunner) ReadReturnValues() error { if !r.RunEnded { return errors.New("Tried to read return values before run ended") } - pointer := virtualMachine.RunContext.Ap + pointer := r.Vm.RunContext.Ap - for i := len(virtualMachine.BuiltinRunners) - 1; i >= 0; i-- { - newPointer, err := virtualMachine.BuiltinRunners[i].FinalStack(&virtualMachine.Segments, pointer) + for i := len(r.Vm.BuiltinRunners) - 1; i >= 0; i-- { + newPointer, err := r.Vm.BuiltinRunners[i].FinalStack(&r.Vm.Segments, pointer) if err != nil { return err } @@ -363,7 +363,7 @@ func (r *CairoRunner) ReadReturnValues(virtualMachine *vm.VirtualMachine) error execBase := r.executionBase begin := pointer.Offset - execBase.Offset - ap := virtualMachine.RunContext.Ap + ap := r.Vm.RunContext.Ap end := ap.Offset - execBase.Offset var publicMemoryExtension []uint @@ -379,26 +379,26 @@ func (r *CairoRunner) ReadReturnValues(virtualMachine *vm.VirtualMachine) error } -func (runner *CairoRunner) CheckUsedCells(virtualMachine *vm.VirtualMachine) error { - for _, builtin := range virtualMachine.BuiltinRunners { +func (runner *CairoRunner) CheckUsedCells() error { + for _, builtin := range runner.Vm.BuiltinRunners { // I guess we call this just in case it errors out, even though later on we also call it? - _, _, err := builtin.GetUsedCellsAndAllocatedSizes(&virtualMachine.Segments, virtualMachine.CurrentStep) + _, _, err := builtin.GetUsedCellsAndAllocatedSizes(&runner.Vm.Segments, runner.Vm.CurrentStep) if err != nil { return err } } - err := runner.CheckRangeCheckUsage(virtualMachine) + err := runner.CheckRangeCheckUsage() if err != nil { return err } - err = runner.CheckMemoryUsage(virtualMachine) + err = runner.CheckMemoryUsage() if err != nil { return err } - err = runner.CheckDilutedCheckUsage(virtualMachine) + err = runner.CheckDilutedCheckUsage() if err != nil { return err } @@ -406,13 +406,13 @@ func (runner *CairoRunner) CheckUsedCells(virtualMachine *vm.VirtualMachine) err return nil } -func (runner *CairoRunner) CheckMemoryUsage(virtualMachine *vm.VirtualMachine) error { +func (runner *CairoRunner) CheckMemoryUsage() error { instance := runner.Layout var builtinsMemoryUnits uint = 0 - for _, builtin := range virtualMachine.BuiltinRunners { - result, err := builtin.GetAllocatedMemoryUnits(&virtualMachine.Segments, virtualMachine.CurrentStep) + for _, builtin := range runner.Vm.BuiltinRunners { + result, err := builtin.GetAllocatedMemoryUnits(&runner.Vm.Segments, runner.Vm.CurrentStep) if err != nil { return err } @@ -420,7 +420,7 @@ func (runner *CairoRunner) CheckMemoryUsage(virtualMachine *vm.VirtualMachine) e builtinsMemoryUnits += result } - totalMemoryUnits := instance.MemoryUnitsPerStep * virtualMachine.CurrentStep + totalMemoryUnits := instance.MemoryUnitsPerStep * runner.Vm.CurrentStep publicMemoryUnits := totalMemoryUnits / instance.PublicMemoryFraction remainder := totalMemoryUnits % instance.PublicMemoryFraction @@ -428,10 +428,10 @@ func (runner *CairoRunner) CheckMemoryUsage(virtualMachine *vm.VirtualMachine) e return errors.Errorf("Total Memory units was not divisible by the Public Memory Fraction. TotalMemoryUnits: %d PublicMemoryFraction: %d", totalMemoryUnits, instance.PublicMemoryFraction) } - instructionMemoryUnits := 4 * virtualMachine.CurrentStep + instructionMemoryUnits := 4 * runner.Vm.CurrentStep unusedMemoryUnits := totalMemoryUnits - (publicMemoryUnits + instructionMemoryUnits + builtinsMemoryUnits) - memoryAddressHoles, err := runner.GetMemoryHoles(virtualMachine) + memoryAddressHoles, err := runner.GetMemoryHoles() if err != nil { return err } @@ -443,11 +443,11 @@ func (runner *CairoRunner) CheckMemoryUsage(virtualMachine *vm.VirtualMachine) e return nil } -func (runner *CairoRunner) GetMemoryHoles(virtualMachine *vm.VirtualMachine) (uint, error) { - return virtualMachine.Segments.GetMemoryHoles(uint(len(virtualMachine.BuiltinRunners))) +func (runner *CairoRunner) GetMemoryHoles() (uint, error) { + return runner.Vm.Segments.GetMemoryHoles(uint(len(runner.Vm.BuiltinRunners))) } -func (runner *CairoRunner) CheckDilutedCheckUsage(virtualMachine *vm.VirtualMachine) error { +func (runner *CairoRunner) CheckDilutedCheckUsage() error { dilutedPoolInstance := runner.Layout.DilutedPoolInstance if dilutedPoolInstance == nil { return nil @@ -455,14 +455,14 @@ func (runner *CairoRunner) CheckDilutedCheckUsage(virtualMachine *vm.VirtualMach var usedUnitsByBuiltins uint = 0 - for _, builtin := range virtualMachine.BuiltinRunners { + for _, builtin := range runner.Vm.BuiltinRunners { usedUnits := builtin.GetUsedDilutedCheckUnits(dilutedPoolInstance.Spacing, dilutedPoolInstance.NBits) ratio := builtin.Ratio() if ratio == 0 { ratio = 1 } - multiplier, err := utils.SafeDiv(virtualMachine.CurrentStep, ratio) + multiplier, err := utils.SafeDiv(runner.Vm.CurrentStep, ratio) if err != nil { return err @@ -471,7 +471,7 @@ func (runner *CairoRunner) CheckDilutedCheckUsage(virtualMachine *vm.VirtualMach usedUnitsByBuiltins += usedUnits * multiplier } - var dilutedUnits uint = dilutedPoolInstance.UnitsPerStep * virtualMachine.CurrentStep + var dilutedUnits uint = dilutedPoolInstance.UnitsPerStep * runner.Vm.CurrentStep var unusedDilutedUnits uint = dilutedUnits - usedUnitsByBuiltins var dilutedUsageUpperBound uint = 1 << dilutedPoolInstance.NBits @@ -483,7 +483,7 @@ func (runner *CairoRunner) CheckDilutedCheckUsage(virtualMachine *vm.VirtualMach return nil } -func (runner *CairoRunner) CheckRangeCheckUsage(virtualMachine *vm.VirtualMachine) error { +func (runner *CairoRunner) CheckRangeCheckUsage() error { var rcMin, rcMax *uint for _, builtin := range runner.Vm.BuiltinRunners { @@ -513,7 +513,7 @@ func (runner *CairoRunner) CheckRangeCheckUsage(virtualMachine *vm.VirtualMachin var rcUnitsUsedByBuiltins uint = 0 for _, builtin := range runner.Vm.BuiltinRunners { - usedUnits, err := builtin.GetUsedPermRangeCheckLimits(&virtualMachine.Segments, virtualMachine.CurrentStep) + usedUnits, err := builtin.GetUsedPermRangeCheckLimits(&runner.Vm.Segments, runner.Vm.CurrentStep) if err != nil { return err } @@ -521,7 +521,7 @@ func (runner *CairoRunner) CheckRangeCheckUsage(virtualMachine *vm.VirtualMachin rcUnitsUsedByBuiltins += usedUnits } - unusedRcUnits := (runner.Layout.RcUnits-3)*virtualMachine.CurrentStep - uint(rcUnitsUsedByBuiltins) + unusedRcUnits := (runner.Layout.RcUnits-3)*runner.Vm.CurrentStep - uint(rcUnitsUsedByBuiltins) if unusedRcUnits < (*rcMax - *rcMin) { return memory.InsufficientAllocatedCellsError(unusedRcUnits, *rcMax-*rcMin) @@ -530,8 +530,7 @@ func (runner *CairoRunner) CheckRangeCheckUsage(virtualMachine *vm.VirtualMachin return nil } -// TODO: Add hint processor when it's done -func (runner *CairoRunner) RunForSteps(steps uint, virtualMachine *vm.VirtualMachine, hintProcessor vm.HintProcessor) error { +func (runner *CairoRunner) RunForSteps(steps uint, hintProcessor vm.HintProcessor) error { hintDataMap, err := runner.BuildHintDataMap(hintProcessor) if err != nil { return err @@ -539,11 +538,11 @@ func (runner *CairoRunner) RunForSteps(steps uint, virtualMachine *vm.VirtualMac constants := runner.Program.ExtractConstants() var remainingSteps int for remainingSteps = int(steps); remainingSteps > 0; remainingSteps-- { - if runner.finalPc != nil && *runner.finalPc == virtualMachine.RunContext.Pc { + if runner.finalPc != nil && *runner.finalPc == runner.Vm.RunContext.Pc { return &vm.VirtualMachineError{Msg: fmt.Sprintf("EndOfProgram: %d", remainingSteps)} } - err := virtualMachine.Step(hintProcessor, &hintDataMap, &constants, &runner.execScopes) + err := runner.Vm.Step(hintProcessor, &hintDataMap, &constants, &runner.execScopes) if err != nil { return err } @@ -552,12 +551,70 @@ func (runner *CairoRunner) RunForSteps(steps uint, virtualMachine *vm.VirtualMac return nil } -// TODO: Add hint processor when it's done -func (runner *CairoRunner) RunUntilSteps(steps uint, virtualMachine *vm.VirtualMachine, hintProcessor vm.HintProcessor) error { - return runner.RunForSteps(steps-virtualMachine.CurrentStep, virtualMachine, hintProcessor) +func (runner *CairoRunner) RunUntilSteps(steps uint, hintProcessor vm.HintProcessor) error { + return runner.RunForSteps(steps-runner.Vm.CurrentStep, hintProcessor) +} + +func (runner *CairoRunner) RunUntilNextPowerOfTwo(hintProcessor vm.HintProcessor) error { + return runner.RunUntilSteps(utils.NextPowOf2(runner.Vm.CurrentStep), hintProcessor) } -// TODO: Add hint processor when it's done -func (runner *CairoRunner) RunUntilNextPowerOfTwo(virtualMachine *vm.VirtualMachine, hintProcessor vm.HintProcessor) error { - return runner.RunUntilSteps(utils.NextPowOf2(virtualMachine.CurrentStep), virtualMachine, hintProcessor) +func (runner *CairoRunner) GetExecutionResources() (ExecutionResources, error) { + nSteps := uint(len(runner.Vm.Trace)) + if nSteps == 0 { + nSteps = runner.Vm.CurrentStep + } + nMemoryHoles, err := runner.GetMemoryHoles() + if err != nil { + return ExecutionResources{}, err + } + builtinInstaceCounter := make(map[string]uint) + for i := 0; i < len(runner.Vm.BuiltinRunners); i++ { + builtinInstaceCounter[runner.Vm.BuiltinRunners[i].Name()], err = runner.Vm.BuiltinRunners[i].GetUsedInstances(&runner.Vm.Segments) + if err != nil { + return ExecutionResources{}, err + } + } + return ExecutionResources{ + NSteps: nSteps, + NMemoryHoles: nMemoryHoles, + BuiltinsInstanceCounter: builtinInstaceCounter, + }, nil +} + +// TODO: Add verifySecure once its implemented +/* +Runs a cairo program from a give entrypoint, indicated by its pc offset, with the given arguments. +If `verifySecure` is set to true, [verifySecureRunner] will be called to run extra verifications. +`programSegmentSize` is only used by the [verifySecureRunner] function and will be ignored if `verifySecure` is set to false. +Each arg can be either MaybeRelocatable, []MaybeRelocatable or [][]MaybeRelocatable +*/ +func (runner *CairoRunner) RunFromEntrypoint(entrypoint uint, args []any, hintProcessor vm.HintProcessor) error { + stack := make([]memory.MaybeRelocatable, 0) + for _, arg := range args { + val, err := runner.Vm.Segments.GenArg(arg) + if err != nil { + return err + } + stack = append(stack, val) + } + returnFp := *memory.NewMaybeRelocatableFelt(lambdaworks.FeltZero()) + end, err := runner.initializeFunctionEntrypoint(entrypoint, &stack, returnFp) + if err != nil { + return err + } + err = runner.initializeVM() + if err != nil { + return err + } + err = runner.RunUntilPC(end, hintProcessor) + if err != nil { + return err + } + err = runner.EndRun(false, false, hintProcessor) + if err != nil { + return err + } + // TODO: verifySecureRunner + return nil } diff --git a/pkg/runners/cairo_runner_test.go b/pkg/runners/cairo_runner_test.go index e19367d1..1c5b7130 100644 --- a/pkg/runners/cairo_runner_test.go +++ b/pkg/runners/cairo_runner_test.go @@ -567,11 +567,10 @@ func TestCheckRangeCheckUsagePermRangeLimitsNone(t *testing.T) { if err != nil { t.Error("Could not initialize Cairo Runner") } - virtualMachine := vm.NewVirtualMachine() - virtualMachine.Trace = make([]vm.TraceEntry, 0) + runner.Vm.Trace = make([]vm.TraceEntry, 0) - err = runner.CheckRangeCheckUsage(virtualMachine) + err = runner.CheckRangeCheckUsage() if err != nil { t.Errorf("Check Range Usage Failed With Error %s", err) } @@ -584,18 +583,17 @@ func TestCheckRangeCheckUsageWithoutBuiltins(t *testing.T) { if err != nil { t.Error("Could not initialize Cairo Runner") } - virtualMachine := vm.NewVirtualMachine() - virtualMachine.Trace = make([]vm.TraceEntry, 0) - virtualMachine.CurrentStep = 1000 - virtualMachine.Segments.Memory.Insert( + runner.Vm.Trace = make([]vm.TraceEntry, 0) + runner.Vm.CurrentStep = 1000 + runner.Vm.Segments.Memory.Insert( memory.NewRelocatable(0, 0), memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromHex("0x80FF80000530")), ) - virtualMachine.Trace = make([]vm.TraceEntry, 1) - virtualMachine.Trace[0] = vm.TraceEntry{Pc: memory.NewRelocatable(0, 0), Ap: memory.NewRelocatable(0, 0), Fp: memory.NewRelocatable(0, 0)} - err = runner.CheckRangeCheckUsage(virtualMachine) + runner.Vm.Trace = make([]vm.TraceEntry, 1) + runner.Vm.Trace[0] = vm.TraceEntry{Pc: memory.NewRelocatable(0, 0), Ap: memory.NewRelocatable(0, 0), Fp: memory.NewRelocatable(0, 0)} + err = runner.CheckRangeCheckUsage() if err != nil { t.Errorf("Check Range Usage Failed With Error %s", err) } @@ -620,7 +618,7 @@ func TestCheckRangeUsageInsufficientAllocatedCells(t *testing.T) { runner.Vm.Trace = make([]vm.TraceEntry, 1) runner.Vm.Trace[0] = vm.TraceEntry{Pc: memory.NewRelocatable(0, 0), Ap: memory.NewRelocatable(0, 0), Fp: memory.NewRelocatable(0, 0)} runner.Vm.Segments.ComputeEffectiveSizes() - err = runner.CheckRangeCheckUsage(&runner.Vm) + err = runner.CheckRangeCheckUsage() if err == nil { t.Error("Check Range Usage Should Have Failed With Insufficient Allocated Cells Error") } @@ -633,11 +631,10 @@ func TestCheckDilutedCheckUsageWithoutPoolInstance(t *testing.T) { if err != nil { t.Error("Could not initialize Cairo Runner") } - virtualMachine := vm.NewVirtualMachine() runner.Layout.DilutedPoolInstance = nil - err = runner.CheckDilutedCheckUsage(virtualMachine) + err = runner.CheckDilutedCheckUsage() if err != nil { t.Errorf("Check Diluted Check Usage Failed With Error %s", err) } @@ -650,12 +647,11 @@ func TestCheckDilutedCheckUsageWithoutBuiltinRunners(t *testing.T) { if err != nil { t.Error("Could not initialize Cairo Runner") } - virtualMachine := vm.NewVirtualMachine() - virtualMachine.CurrentStep = 10000 - virtualMachine.BuiltinRunners = make([]builtins.BuiltinRunner, 0) + runner.Vm.CurrentStep = 10000 + runner.Vm.BuiltinRunners = make([]builtins.BuiltinRunner, 0) - err = runner.CheckDilutedCheckUsage(virtualMachine) + err = runner.CheckDilutedCheckUsage() if err != nil { t.Errorf("Check Diluted Check Usage Failed With Error %s", err) } @@ -668,12 +664,11 @@ func TestCheckDilutedCheckUsageInsufficientAllocatedCells(t *testing.T) { if err != nil { t.Error("Could not initialize Cairo Runner") } - virtualMachine := vm.NewVirtualMachine() - virtualMachine.CurrentStep = 100 - virtualMachine.BuiltinRunners = make([]builtins.BuiltinRunner, 0) + runner.Vm.CurrentStep = 100 + runner.Vm.BuiltinRunners = make([]builtins.BuiltinRunner, 0) - err = runner.CheckDilutedCheckUsage(virtualMachine) + err = runner.CheckDilutedCheckUsage() if err == nil { t.Errorf("Check Diluted Check Usage Should Have failed With Insufficient Allocated Cells Error") } @@ -686,13 +681,12 @@ func TestCheckDilutedCheckUsage(t *testing.T) { if err != nil { t.Error("Could not initialize Cairo Runner") } - virtualMachine := vm.NewVirtualMachine() - virtualMachine.CurrentStep = 8192 - virtualMachine.BuiltinRunners = make([]builtins.BuiltinRunner, 0) - virtualMachine.BuiltinRunners = append(virtualMachine.BuiltinRunners, builtins.NewBitwiseBuiltinRunner(256)) + runner.Vm.CurrentStep = 8192 + runner.Vm.BuiltinRunners = make([]builtins.BuiltinRunner, 0) + runner.Vm.BuiltinRunners = append(runner.Vm.BuiltinRunners, builtins.NewBitwiseBuiltinRunner(256)) - err = runner.CheckDilutedCheckUsage(virtualMachine) + err = runner.CheckDilutedCheckUsage() if err != nil { t.Errorf("Check Diluted Check Usage Failed With Error %s", err) } @@ -706,14 +700,62 @@ func TestCheckUsedCellsDilutedCheckUsageError(t *testing.T) { if err != nil { t.Error("Could not initialize Cairo Runner") } - virtualMachine := vm.NewVirtualMachine() - virtualMachine.Segments.SegmentUsedSizes = make(map[uint]uint) - virtualMachine.Segments.SegmentUsedSizes[0] = 4 - virtualMachine.Trace = []vm.TraceEntry{} + runner.Vm.Segments.SegmentUsedSizes = make(map[uint]uint) + runner.Vm.Segments.SegmentUsedSizes[0] = 4 + runner.Vm.Trace = []vm.TraceEntry{} - err = runner.CheckUsedCells(virtualMachine) + err = runner.CheckUsedCells() if err == nil { t.Errorf("Check Used Cells Should Have failed With Insufficient Allocated Cells Error") } } + +func TestRunFibonacciGetExecutionResources(t *testing.T) { + cairoRunConfig := cairo_run.CairoRunConfig{Layout: "all_cairo", ProofMode: false} + runner, err := cairo_run.CairoRun("../../cairo_programs/fibonacci.json", cairoRunConfig) + if err != nil { + t.Errorf("Program execution failed with error: %s", err) + } + expectedExecutionResources := runners.ExecutionResources{ + NSteps: 80, + BuiltinsInstanceCounter: make(map[string]uint), + } + executionResources, _ := runner.GetExecutionResources() + if !reflect.DeepEqual(executionResources, expectedExecutionResources) { + t.Errorf("Wong ExecutionResources.\n Expected : %+v, got: %+v", expectedExecutionResources, executionResources) + } +} + +// This test will run the `fib` function in the fibonacci.json program +func TestRunFromEntryPointFibonacci(t *testing.T) { + compiledProgram, _ := parser.Parse("../../cairo_programs/fibonacci.json") + programJson := vm.DeserializeProgramJson(compiledProgram) + + entrypoint := programJson.Identifiers["__main__.fib"].PC + args := []any{ + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltOne()), + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltOne()), + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(10)), + } + runner, _ := runners.NewCairoRunner(programJson, "all_cairo", false) + hintProcessor := hints.CairoVmHintProcessor{} + + runner.InitializeBuiltins() + runner.InitializeSegments() + err := runner.RunFromEntrypoint(uint(entrypoint), args, &hintProcessor) + + if err != nil { + t.Errorf("Running fib entrypoint failed with error %s", err.Error()) + } + + // Check result + res, err := runner.Vm.GetReturnValues(1) + if err != nil { + t.Errorf("Failed to fetch return values from fib with error %s", err.Error()) + } + if len(res) != 1 || !reflect.DeepEqual(res[0], *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(144))) { + t.Errorf("Wrong value returned by fib entrypoint.\n Expected [144], got: %+v", res) + } + +} diff --git a/pkg/runners/execution_resources.go b/pkg/runners/execution_resources.go new file mode 100644 index 00000000..e3872021 --- /dev/null +++ b/pkg/runners/execution_resources.go @@ -0,0 +1,7 @@ +package runners + +type ExecutionResources struct { + NSteps uint + NMemoryHoles uint + BuiltinsInstanceCounter map[string]uint +} diff --git a/pkg/vm/cairo_run/cairo_run.go b/pkg/vm/cairo_run/cairo_run.go index 678a658d..d54a1f99 100644 --- a/pkg/vm/cairo_run/cairo_run.go +++ b/pkg/vm/cairo_run/cairo_run.go @@ -53,18 +53,18 @@ func CairoRun(programPath string, cairoRunConfig CairoRunConfig) (*runners.Cairo if err != nil { return nil, err } - err = cairoRunner.EndRun(cairoRunConfig.DisableTracePadding, false, &cairoRunner.Vm, &hintProcessor) + err = cairoRunner.EndRun(cairoRunConfig.DisableTracePadding, false, &hintProcessor) if err != nil { return nil, err } - err = cairoRunner.ReadReturnValues(&cairoRunner.Vm) + err = cairoRunner.ReadReturnValues() if err != nil { return nil, err } if cairoRunConfig.ProofMode { - cairoRunner.FinalizeSegments(cairoRunner.Vm) + cairoRunner.FinalizeSegments() } err = cairoRunner.Vm.Relocate() diff --git a/pkg/vm/memory/segments.go b/pkg/vm/memory/segments.go index 732ee0b7..32c5acda 100644 --- a/pkg/vm/memory/segments.go +++ b/pkg/vm/memory/segments.go @@ -1,6 +1,8 @@ package memory import ( + "errors" + "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks" ) @@ -186,3 +188,39 @@ func (m *MemorySegmentManager) GetFeltRange(start Relocatable, size uint) ([]lam } return feltRange, nil } + +/* +Converts a generic argument into a MaybeRelocatable +If the argument is a slice, it loads it into memory in a new segment and returns its base +Accepts MaybeRelocatable, []MaybeRelocatable, [][]MaybeRelocatable +*/ +func (m *MemorySegmentManager) GenArg(arg any) (MaybeRelocatable, error) { + // Attempt to cast to MaybeRelocatable + a, ok := arg.(MaybeRelocatable) + if ok { + return a, nil + } + // Attempt to cast to []MaybeRelocatable + data, ok := arg.([]MaybeRelocatable) + if ok { + base := m.AddSegment() + _, err := m.LoadData(base, &data) + return *NewMaybeRelocatableRelocatable(base), err + } + // Attempt to cast to [][]MaybeRelocatable + datas, ok := arg.([][]MaybeRelocatable) + if ok { + args := make([]MaybeRelocatable, 0) + for _, data = range datas { + dataBase, err := m.GenArg(data) + if err != nil { + return *NewMaybeRelocatableFelt(lambdaworks.FeltZero()), err + } + args = append(args, dataBase) + } + base := m.AddSegment() + _, err := m.LoadData(base, &args) + return *NewMaybeRelocatableRelocatable(base), err + } + return *NewMaybeRelocatableFelt(lambdaworks.FeltZero()), errors.New("GenArg: found argument of invalid type.") +} diff --git a/pkg/vm/memory/segments_test.go b/pkg/vm/memory/segments_test.go index cdca3585..e4604188 100644 --- a/pkg/vm/memory/segments_test.go +++ b/pkg/vm/memory/segments_test.go @@ -310,3 +310,52 @@ func TestGetFeltRangeRelocatable(t *testing.T) { t.Errorf("GetFeltRange should have failed") } } + +func TestGenArgMaybeRelocatable(t *testing.T) { + segments := memory.NewMemorySegmentManager() + arg := any(*memory.NewMaybeRelocatableFelt(lambdaworks.FeltZero())) + expectedArg := *memory.NewMaybeRelocatableFelt(lambdaworks.FeltZero()) + genedArg, err := segments.GenArg(arg) + if err != nil || !reflect.DeepEqual(expectedArg, genedArg) { + t.Error("GenArg failed or returned wrong value") + } +} + +func TestGenArgSliceMaybeRelocatable(t *testing.T) { + segments := memory.NewMemorySegmentManager() + arg := any([]memory.MaybeRelocatable{*memory.NewMaybeRelocatableFelt(lambdaworks.FeltZero())}) + + expectedBase := memory.NewRelocatable(0, 0) + expectedArg := *memory.NewMaybeRelocatableRelocatable(expectedBase) + genedArg, err := segments.GenArg(arg) + if err != nil || !reflect.DeepEqual(expectedArg, genedArg) { + t.Error("GenArg failed or returned wrong value") + } + val, err := segments.Memory.GetFelt(expectedBase) + if err != nil || !val.IsZero() { + t.Error("GenArg inserted wrong value into memory") + } +} + +func TestGenArgSliceSliceMaybeRelocatable(t *testing.T) { + segments := memory.NewMemorySegmentManager() + arg := any([][]memory.MaybeRelocatable{{*memory.NewMaybeRelocatableFelt(lambdaworks.FeltZero())}}) + + expectedBaseA := memory.NewRelocatable(1, 0) + expectedBaseB := memory.NewRelocatable(0, 0) + expectedArg := *memory.NewMaybeRelocatableRelocatable(expectedBaseA) + genedArg, err := segments.GenArg(arg) + + if err != nil || !reflect.DeepEqual(expectedArg, genedArg) { + t.Error("GenArg failed or returned wrong value") + } + valA, err := segments.Memory.GetRelocatable(expectedBaseA) + if err != nil || valA != expectedBaseB { + t.Error("GenArg inserted wrong value into memory") + } + + valB, err := segments.Memory.GetFelt(expectedBaseB) + if err != nil || !valB.IsZero() { + t.Error("GenArg inserted wrong value into memory") + } +} diff --git a/pkg/vm/vm_core.go b/pkg/vm/vm_core.go index b8aa1e2f..c5cb69b1 100644 --- a/pkg/vm/vm_core.go +++ b/pkg/vm/vm_core.go @@ -642,3 +642,12 @@ func (vm *VirtualMachine) GetRangeCheckBound() (lambdaworks.Felt, error) { return rcBuiltin.Bound(), nil } + +// Gets `nRet` return values from memory +func (vm *VirtualMachine) GetReturnValues(nRet uint) ([]memory.MaybeRelocatable, error) { + ptr, err := vm.RunContext.Ap.SubUint(nRet) + if err != nil { + return nil, err + } + return vm.Segments.Memory.GetRange(ptr, nRet) +} diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 8f3ab08c..1d537321 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1088,3 +1088,31 @@ func TestGetFooBuiltinReturnsNilAndError(t *testing.T) { t.Error("Obtained a non existant builtin, or didn't raise an error") } } + +func TestReadReturnValuesOk(t *testing.T) { + vm := vm.NewVirtualMachine() + vm.Segments.AddSegment() + // Load data at ap and advance ap + data := []memory.MaybeRelocatable{ + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(1)), + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(2)), + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(3)), + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(4)), + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(5)), + } + vm.RunContext.Ap, _ = vm.Segments.LoadData(vm.RunContext.Ap, &data) + // Fetch 3 return values + expectedReturnValues := []memory.MaybeRelocatable{ + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(3)), + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(4)), + *memory.NewMaybeRelocatableFelt(lambdaworks.FeltFromUint(5)), + } + returnValues, err := vm.GetReturnValues(3) + if err != nil { + t.Errorf("GetReturnValues failed with error: %s", err.Error()) + } + + if !reflect.DeepEqual(expectedReturnValues, returnValues) { + t.Errorf("Wrong return values.\n Expected: %+v, got: %+v", expectedReturnValues, returnValues) + } +}