Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/NethermindEth/cairo-vm-go i…
Browse files Browse the repository at this point in the history
…nto cairo1_parse_instructions
  • Loading branch information
MaksymMalicki committed Oct 22, 2024
2 parents b6bc41e + 167dadd commit 3091ab5
Show file tree
Hide file tree
Showing 51 changed files with 2,847 additions and 557 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
python-version: "3.9"

- name: Install cairo-lang
run: pip install cairo-lang==0.13.1
run: pip install cairo-lang==0.13.2

- name: Install sympy
run: pip install sympy==1.11.1
Expand Down
63 changes: 61 additions & 2 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package main

import (
"encoding/json"
"fmt"
"math"
"os"
"path/filepath"

"github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/core"
"github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter"
hintrunner "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/zero"
cairoversion "github.com/NethermindEth/cairo-vm-go/pkg/parsers/cairo_version"
"github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet"
zero "github.com/NethermindEth/cairo-vm-go/pkg/parsers/zero"
runnerzero "github.com/NethermindEth/cairo-vm-go/pkg/runners/zero"
"github.com/NethermindEth/cairo-vm-go/pkg/runner"
"github.com/urfave/cli/v2"
)

Expand All @@ -24,6 +25,8 @@ func main() {
var traceLocation string
var memoryLocation string
var layoutName string
var airPublicInputLocation string
var airPrivateInputLocation string
app := &cli.App{
Name: "cairo-vm",
Usage: "A cairo virtual machine",
Expand Down Expand Up @@ -85,6 +88,18 @@ func main() {
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 {
// TODO: move this action's body to a separate function to decrease the
Expand Down Expand Up @@ -136,6 +151,10 @@ func main() {
case proofmode && cairoVersion == 0:
runnerMode = runnerzero.ProofModeCairo0
}
program, err := runner.LoadCairoZeroProgram(zeroProgram)
if err != nil {
return fmt.Errorf("cannot load program: %w", err)
}
fmt.Println("Running....")
runner, err := runnerzero.NewRunner(runnerMode, program, hints, proofmode, collectTrace, maxsteps, layoutName)
if err != nil {
Expand Down Expand Up @@ -191,6 +210,46 @@ func main() {
}
}

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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
%builtins keccak
from starkware.cairo.common.cairo_builtins import KeccakBuiltin
from starkware.cairo.common.keccak_state import KeccakBuiltinState

func main{keccak_ptr: KeccakBuiltin*}() {
assert keccak_ptr[0].input = KeccakBuiltinState(1, 2, 3, 4, 5, 6, 7, 8);
let result = keccak_ptr[0].output;
let keccak_ptr = keccak_ptr + KeccakBuiltin.SIZE;
assert result.s0 = 528644516554364142278482415480021626364691973678134577961206;
assert result.s1 = 768681319646568210457759892191562701823009052229295869963057;
assert result.s2 = 1439835513376369408063324968379272676079109225238241190228026;
assert result.s3 = 1150396629165612276474514703759718478742374517669870754478270;
assert result.s4 = 1515147102575186161827863034255579930572231617017100845406254;
assert result.s5 = 1412568161597072838250338588041800080889949791225997426843744;
assert result.s6 = 982235455376248641031519404605670648838699214888770304613539;
assert result.s7 = 1339947803093378278438908448344904300127577306141693325151040;
return ();
}
148 changes: 130 additions & 18 deletions integration_tests/cairozero_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -53,7 +52,7 @@ func (f *Filter) filtered(testFile string) bool {
return false
}

func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[string][2]int, benchmark bool, errorExpected bool) {
func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[string][3]int, benchmark bool, errorExpected bool) {
t.Logf("testing: %s\n", path)

compiledOutput, err := compileZeroCode(path)
Expand All @@ -73,6 +72,17 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str
}
}

elapsedRs, rsTraceFile, rsMemoryFile, err := runRustVm(name, compiledOutput)
if errorExpected {
// we let the code go on so that we can check if the go vm also raises an error
assert.Error(t, err, path)
} else {
if err != nil {
t.Error(err)
return
}
}

elapsedGo, traceFile, memoryFile, _, err := runVm(compiledOutput)
if errorExpected {
assert.Error(t, err, path)
Expand All @@ -85,7 +95,7 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str
}

if benchmark {
benchmarkMap[name] = [2]int{int(elapsedPy.Milliseconds()), int(elapsedGo.Milliseconds())}
benchmarkMap[name] = [3]int{int(elapsedPy.Milliseconds()), int(elapsedGo.Milliseconds()), int(elapsedRs.Milliseconds())}
}

pyTrace, pyMemory, err := decodeProof(pyTraceFile, pyMemoryFile)
Expand All @@ -100,6 +110,20 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str
return
}

rsTrace, rsMemory, err := decodeProof(rsTraceFile, rsMemoryFile)
if err != nil {
t.Error(err)
return
}

if !assert.Equal(t, pyTrace, rsTrace) {
t.Logf("pytrace:\n%s\n", traceRepr(pyTrace))
t.Logf("rstrace:\n%s\n", traceRepr(rsTrace))
}
if !assert.Equal(t, pyMemory, rsMemory) {
t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory))
t.Logf("rsmemory;\n%s\n", memoryRepr(rsMemory))
}
if !assert.Equal(t, pyTrace, trace) {
t.Logf("pytrace:\n%s\n", traceRepr(pyTrace))
t.Logf("trace:\n%s\n", traceRepr(trace))
Expand All @@ -108,6 +132,14 @@ func runAndTestFile(t *testing.T, path string, name string, benchmarkMap map[str
t.Logf("pymemory;\n%s\n", memoryRepr(pyMemory))
t.Logf("memory;\n%s\n", memoryRepr(memory))
}
if !assert.Equal(t, rsTrace, trace) {
t.Logf("rstrace:\n%s\n", traceRepr(rsTrace))
t.Logf("trace:\n%s\n", traceRepr(trace))
}
if !assert.Equal(t, rsMemory, memory) {
t.Logf("rsmemory;\n%s\n", memoryRepr(rsMemory))
t.Logf("memory;\n%s\n", memoryRepr(memory))
}
}

var zerobench = flag.Bool("zerobench", false, "run integration tests and generate benchmarks file")
Expand All @@ -123,7 +155,7 @@ func TestCairoZeroFiles(t *testing.T) {
filter := Filter{}
filter.init()

benchmarkMap := make(map[string][2]int)
benchmarkMap := make(map[string][3]int)

sem := make(chan struct{}, 5) // semaphore to limit concurrency
var wg sync.WaitGroup // WaitGroup to wait for all goroutines to finish
Expand Down Expand Up @@ -176,35 +208,31 @@ func TestCairoZeroFiles(t *testing.T) {
}
}

// Save the Benchmarks for the integration tests in `BenchMarks.txt`
func WriteBenchMarksToFile(benchmarkMap map[string][2]int) {
totalWidth := 123
func WriteBenchMarksToFile(benchmarkMap map[string][3]int) {
totalWidth := 113 // Reduced width to adjust for long file names

border := strings.Repeat("=", totalWidth)
separator := strings.Repeat("-", totalWidth)

var sb strings.Builder
w := tabwriter.NewWriter(&sb, 40, 0, 0, ' ', tabwriter.Debug)
w := tabwriter.NewWriter(&sb, 0, 0, 1, ' ', tabwriter.AlignRight)

sb.WriteString(border + "\n")
fmt.Fprintln(w, "| File \t PythonVM (ms) \t GoVM (ms) \t")
fmt.Fprintf(w, "| %-40s | %-20s | %-20s | %-20s |\n", "File", "PythonVM (ms)", "GoVM (ms)", "RustVM (ms)")
w.Flush()
sb.WriteString(border + "\n")

iterator := 0
totalFiles := len(benchmarkMap)

for key, values := range benchmarkMap {
row := "| " + key + "\t "

for iter, value := range values {
row = row + strconv.Itoa(value) + "\t"
if iter == 0 {
row = row + " "
}
// Adjust the key length if it's too long
displayKey := key
if len(displayKey) > 40 {
displayKey = displayKey[:37] + "..."
}

fmt.Fprintln(w, row)
fmt.Fprintf(w, "| %-40s | %-20d | %-20d | %-20d |\n", displayKey, values[0], values[1], values[2])
w.Flush()

if iterator < totalFiles-1 {
Expand Down Expand Up @@ -236,6 +264,8 @@ const (
compiledSuffix = "_compiled.json"
pyTraceSuffix = "_py_trace"
pyMemorySuffix = "_py_memory"
rsTraceSuffix = "_rs_trace"
rsMemorySuffix = "_rs_memory"
traceSuffix = "_trace"
memorySuffix = "_memory"
)
Expand Down Expand Up @@ -288,8 +318,22 @@ func runPythonVm(testFilename, path string) (time.Duration, string, string, erro
// A file without this suffix will use the default ("plain") layout.
if strings.HasSuffix(testFilename, ".small.cairo") {
args = append(args, "--layout", "small")
} else if strings.HasSuffix(testFilename, ".dex.cairo") {
args = append(args, "--layout", "dex")
} else if strings.HasSuffix(testFilename, ".recursive.cairo") {
args = append(args, "--layout", "recursive")
} else if strings.HasSuffix(testFilename, ".starknet_with_keccak.cairo") {
args = append(args, "--layout", "starknet_with_keccak")
} else if strings.HasSuffix(testFilename, ".starknet.cairo") {
args = append(args, "--layout", "starknet")
} else if strings.HasSuffix(testFilename, ".recursive_large_output.cairo") {
args = append(args, "--layout", "recursive_large_output")
} else if strings.HasSuffix(testFilename, ".recursive_with_poseidon.cairo") {
args = append(args, "--layout", "recursive_with_poseidon")
} else if strings.HasSuffix(testFilename, ".all_solidity.cairo") {
args = append(args, "--layout", "all_solidity")
} else if strings.HasSuffix(testFilename, ".all_cairo.cairo") {
args = append(args, "--layout", "all_cairo")
}

cmd := exec.Command("cairo-run", args...)
Expand All @@ -309,6 +353,61 @@ func runPythonVm(testFilename, path string) (time.Duration, string, string, erro
return elapsed, traceOutput, memoryOutput, nil
}

// given a path to a compiled cairo zero file, execute it using the
// rust vm and return the trace and memory files location
func runRustVm(testFilename, path string) (time.Duration, string, string, error) {
traceOutput := swapExtenstion(path, rsTraceSuffix)
memoryOutput := swapExtenstion(path, rsMemorySuffix)

args := []string{
path,
"--proof_mode",
"--trace_file",
traceOutput,
"--memory_file",
memoryOutput,
}

// If any other layouts are needed, add the suffix checks here.
// The convention would be: ".$layout.cairo"
// A file without this suffix will use the default ("plain") layout.
if strings.HasSuffix(testFilename, ".small.cairo") {
args = append(args, "--layout", "small")
} else if strings.HasSuffix(testFilename, ".dex.cairo") {
args = append(args, "--layout", "dex")
} else if strings.HasSuffix(testFilename, ".recursive.cairo") {
args = append(args, "--layout", "recursive")
} else if strings.HasSuffix(testFilename, ".starknet_with_keccak.cairo") {
args = append(args, "--layout", "starknet_with_keccak")
} else if strings.HasSuffix(testFilename, ".starknet.cairo") {
args = append(args, "--layout", "starknet")
} else if strings.HasSuffix(testFilename, ".recursive_large_output.cairo") {
args = append(args, "--layout", "recursive_large_output")
} else if strings.HasSuffix(testFilename, ".recursive_with_poseidon.cairo") {
args = append(args, "--layout", "recursive_with_poseidon")
} else if strings.HasSuffix(testFilename, ".all_solidity.cairo") {
args = append(args, "--layout", "all_solidity")
} else if strings.HasSuffix(testFilename, ".all_cairo.cairo") {
args = append(args, "--layout", "all_cairo")
}

cmd := exec.Command("./../rust_vm_bin/cairo-vm-cli", args...)

start := time.Now()

res, err := cmd.CombinedOutput()

elapsed := time.Since(start)

if err != nil {
return 0, "", "", fmt.Errorf(
"./../rust_vm_bin/cairo-vm-cli %s: %w\n%s", path, err, string(res),
)
}

return elapsed, traceOutput, memoryOutput, nil
}

// given a path to a compiled cairo zero file, execute
// it using our vm
func runVm(path string) (time.Duration, string, string, string, error) {
Expand All @@ -321,8 +420,22 @@ func runVm(path string) (time.Duration, string, string, string, error) {
layout := "plain"
if strings.Contains(path, ".small") {
layout = "small"
} else if strings.Contains(path, ".dex") {
layout = "dex"
} else if strings.Contains(path, ".recursive") {
layout = "recursive"
} else if strings.Contains(path, ".starknet_with_keccak") {
layout = "starknet_with_keccak"
} else if strings.Contains(path, ".starknet") {
layout = "starknet"
} else if strings.Contains(path, ".recursive_large_output") {
layout = "recursive_large_output"
} else if strings.Contains(path, ".recursive_with_poseidon") {
layout = "recursive_with_poseidon"
} else if strings.Contains(path, ".all_solidity") {
layout = "all_solidity"
} else if strings.Contains(path, ".all_cairo") {
layout = "all_cairo"
}

cmd := exec.Command(
Expand Down Expand Up @@ -351,7 +464,6 @@ func runVm(path string) (time.Duration, string, string, string, error) {
}

return elapsed, traceOutput, memoryOutput, string(res), nil

}

func decodeProof(traceLocation string, memoryLocation string) ([]vm.Trace, []*fp.Element, error) {
Expand Down
Loading

0 comments on commit 3091ab5

Please sign in to comment.