Skip to content

Commit

Permalink
Add ExecutionResources + fix CairoRunner methods that received vm…
Browse files Browse the repository at this point in the history
… 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 <[email protected]>
  • Loading branch information
fmoletta and pefontana authored Sep 29, 2023
1 parent 8c01a73 commit 78c0a08
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 91 deletions.
171 changes: 114 additions & 57 deletions pkg/runners/cairo_runner.go

Large diffs are not rendered by default.

104 changes: 73 additions & 31 deletions pkg/runners/cairo_runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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")
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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")
}
Expand All @@ -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)
}
Expand All @@ -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)
}

}
7 changes: 7 additions & 0 deletions pkg/runners/execution_resources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package runners

type ExecutionResources struct {
NSteps uint
NMemoryHoles uint
BuiltinsInstanceCounter map[string]uint
}
6 changes: 3 additions & 3 deletions pkg/vm/cairo_run/cairo_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
38 changes: 38 additions & 0 deletions pkg/vm/memory/segments.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package memory

import (
"errors"

"github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks"
)

Expand Down Expand Up @@ -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.")
}
49 changes: 49 additions & 0 deletions pkg/vm/memory/segments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
9 changes: 9 additions & 0 deletions pkg/vm/vm_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
28 changes: 28 additions & 0 deletions pkg/vm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit 78c0a08

Please sign in to comment.