Skip to content

Commit

Permalink
feat: add basic cli (#85)
Browse files Browse the repository at this point in the history
* dev: add cli packages

* feat: add basic cli

* dev: remove figlet

* feat: add error handling and flag conflicts

* doc: add telegram group to README

* chore: downgrade trunk check to python 3.10 for consistency with tests action

* doc: add CLI usage to README

* chore: move Usage after Cairo presentation

* doc: add cli usage example

* doc: add state of builtins implem

* fix: use Op1Src enum for instruction initialization

* chore: remove unused local variable

* feat: add package build script

* chore: add types to print trace variables

* chore: remove empty cli file

* chore: rename memory error to avoid export conflicts

* feat: add build script with type declaration transforms

* refactor: export argument json validation to argument argParser

* doc: update doc for local dependency use

* feat: rename cli to cairo

* refactor: rename relocateOffset to offset

* doc: use updated cli command name

* chore: remove unused scripts

* chore: format RunOptions

* refactor: wrap cli action in a global try catch clause
  • Loading branch information
zmalatrax authored Jun 12, 2024
1 parent ef370e6 commit c713512
Show file tree
Hide file tree
Showing 15 changed files with 357 additions and 134 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/trunk-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

# Adding cairo-format
- name: Set up Python 3.11
uses: actions/setup-python@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: '3.10'
cache: pip
- run: pip install cairo-lang

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
dist

cairo_programs/**/*.json
55 changes: 50 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# TypeScript Cairo VM
# Cairo VM Typescript

<div align="center">
<h1><code>TypeScript Cairo VM</code></h1>
<h1><code>Cairo VM TypeScript</code></h1>

<strong>An implementation of the Cairo VM in TypeScript, focusing on
education</strong>

[Github](https://github.com/kkrt-labs/cairo-vm-ts)
[Github](https://github.com/kkrt-labs/cairo-vm-ts) ·
[Telegram](https://t.me/cairovmts)

<sub>Built with 🥕 by <a href="https://twitter.com/KakarotZkEvm">KKRT
Labs</a></sub>
Expand Down Expand Up @@ -92,13 +93,44 @@ performance and safety. While the ones of our TypeScript implementation is
- Deliberate design choices to further improve readability and simplicity
- Extensive documentation: JSDoc, diagrams, explainers, etc.

## Usage

### CLI

You can install the CLI `cairo-vm-ts` by doing the following:

1. Clone this repo: `git clone [email protected]:kkrt-labs/cairo-vm-ts.git`
2. Go to the cloned directory: `cd cairo-vm-ts`
3. Install the dependencies: `bun install`
4. Register the package as a _linkable_ package: `bun link`

Example usage:

```bash
cairo run fibonacci.json --export-memory fib_mem.bin --print-memory --print-output
```

### As a dependency

No package release has been done yet.

You can still add it as a dependency with a local copy:

1. Clone this repo: `git clone [email protected]:kkrt-labs/cairo-vm-ts.git`
2. Go to the cloned directory: `cd cairo-vm-ts`
3. Install the dependencies: `bun install`
4. Build the project: `bun run build`
5. Go to your project `cd ~/my-project`
6. Add `cairo-vm-ts` to your project dependency:
`<bun | yarn | npm> add ~/path/to/cairo-vm-ts`

## State of the VM

| Goals | Done? |
| ---------------------------- | ------- |
| Run basic Cairo Zero program | &#9745; |
| Run basic Cairo program | &#9744; |
| Add [builtins](#builtins) | &#9744; |
| Add [builtins](#builtins) | &#9745; |
| Add [hints](#hints) | &#9744; |
| Run StarkNet contracts | &#9744; |
| Benchmark against other VMs | &#9744; |
Expand All @@ -107,7 +139,20 @@ performance and safety. While the ones of our TypeScript implementation is

### Builtins

<!-- Add a table with the builtin list and state done/to be done -->
| Builtin | Done? |
| -------------------------------------------------------------------- | ------- |
| [Output](https://github.com/kkrt-labs/cairo-vm-ts/issues/65) | &#9745; |
| [Pedersen](https://github.com/kkrt-labs/cairo-vm-ts/issues/70) | &#9745; |
| [Range Check](https://github.com/kkrt-labs/cairo-vm-ts/issues/68) | &#9745; |
| [ECDSA](https://github.com/kkrt-labs/cairo-vm-ts/issues/67) | &#9745; |
| [Bitwise](https://github.com/kkrt-labs/cairo-vm-ts/issues/62) | &#9745; |
| [EcOp](https://github.com/kkrt-labs/cairo-vm-ts/issues/66) | &#9745; |
| [Keccak](https://github.com/kkrt-labs/cairo-vm-ts/issues/69) | &#9745; |
| [Poseidon](https://github.com/kkrt-labs/cairo-vm-ts/issues/71) | &#9745; |
| [Range Check 96](https://github.com/kkrt-labs/cairo-vm-ts/issues/81) | &#9745; |
| Segment Arena | &#9744; |
| AddMod | &#9744; |
| MulMod | &#9744; |

### Hints

Expand Down
Binary file modified bun.lockb
Binary file not shown.
38 changes: 30 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@
"name": "cairo-vm-ts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "echo \"Error: no entrypoint yet\" && exit 1"
},
"keywords": [],
"author": "Clément Walter <[email protected]>",
"license": "Apache-2.0",
"devDependencies": {
"bun-types": "^1.1.12",
"typescript": "^5.2.2"
"main": "dist/index.js",
"types": "dist/types/index.d.ts",
"scripts": {
"clean": "rimraf dist",
"prebuild": "bun run clean",
"build": "bun build --target=node ./src/index.ts --outfile=dist/index.js && bun run build:declaration",
"build:declaration": "tspc --emitDeclarationOnly --project tsconfig.types.json",
"postbuild": "rimraf tsconfig.types.tsbuildinfo"
},
"repository": {
"type": "git",
"url": "https://github.com/kkrt-labs/cairo-vm-ts"
},
"files": [
"src/**/*.ts",
"dist/*.js",
"dist/types/**/*.d.ts"
],
"prettier": {
"proseWrap": "always",
"trailingComma": "es5",
Expand All @@ -23,8 +32,21 @@
"printWidth": 80
},
"dependencies": {
"@commander-js/extra-typings": "^12.1.0",
"@noble/curves": "^1.4.0",
"@scure/starknet": "^1.0.0",
"commander": "^12.1.0",
"consola": "^3.2.3",
"rimraf": "^5.0.7",
"zod": "canary"
},
"devDependencies": {
"bun-types": "^1.1.12",
"ts-patch": "^3.2.0",
"typescript": "^5.2.2",
"typescript-transform-paths": "^3.4.7"
},
"bin": {
"cairo": "./src/cli.ts"
}
}
137 changes: 137 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#! /usr/bin/env bun

import * as fs from 'fs';
import { Command, Option } from '@commander-js/extra-typings';

import { consola } from 'consola';
import { parseProgram } from 'vm/program';
import { CairoRunner, RunOptions } from 'runners/cairoRunner';
import { TraceEntry } from 'vm/virtualMachine';
import { Argument } from 'commander';

consola.options = {
...consola.options,
formatOptions: {
date: false,
},
};

const VERSION_CLI = '0.1.0';

const program = new Command().name('cairo').version(VERSION_CLI);

program
.command('run', { isDefault: true })
.description('Run a compiled Cairo program')
.addArgument(
new Argument(
'<program.json>',
'path to Cairo compilation artifacts'
).argParser((path) => {
if (!path.match(/\.json$/))
throw new Error('Provided file is not a JSON');
return path;
})
)
.option('--no-relocate', 'do not relocate memory')
.addOption(
new Option(
'--offset <OFFSET>',
'start address of the relocated memory\nStarkWare verifier expects offset to be 1'
)
.default(0, '0')
.argParser(parseInt)
)
.option(
'--export-trace <TRACE_FILENAME>',
'export the trace, little-endian encoded'
)
.option(
'--export-memory <MEMORY_FILENAME>',
'export the relocated memory, little-endian encoded'
)
.option('--print-trace', 'print the trace')
.option('--print-memory', 'print the non-relocated memory')
.option('--print-relocated-memory', 'print the relocated memory')
.option('--print-output', 'print the output segment')
.action(async (path, options) => {
try {
const {
relocate,
offset,
exportMemory,
exportTrace,
printOutput,
printMemory,
printRelocatedMemory,
printTrace,
} = options;

if (
(!relocate && !!offset) ||
(!relocate && exportMemory) ||
(!relocate && printRelocatedMemory)
) {
consola.log(
"option '--no-relocate' cannot be used with options '--offset <OFFSET>', '--export-memory <MEMORY_FILENAME>' or '--print-relocated-memory'"
);
process.exit(1);
}

consola.info(`Cairo VM TS ${VERSION_CLI} - Execution Mode`);

const program = parseProgram(fs.readFileSync(String(path), 'utf-8'));
const runner = new CairoRunner(program);
const config: RunOptions = { relocate: relocate, offset: offset };
runner.run(config);
consola.success('Execution finished!');

if (exportMemory) {
consola.info('Exporting memory...');
runner.exportMemory(exportMemory, offset);
consola.success(`Memory exported to ${exportMemory}`);
}
if (exportTrace) {
consola.info('Exporting trace...');
runner.exportTrace(exportTrace);
consola.success(`Trace exported to ${exportTrace}`);
}

if (printMemory) consola.log(runner.vm.memory.toString());
if (printRelocatedMemory)
consola.log(runner.vm.relocatedMemoryToString());
if (printTrace)
consola.log(
'\nTRACE:',
runner.vm.trace
.map((entry: TraceEntry, index: number) =>
[
`\nSTEP: ${index}`,
`pc: ${entry.pc.toString()}`,
`ap: ${entry.ap.toString()}`,
`fp: ${entry.fp.toString()}\n`,
].join('\n')
)
.join('\n')
);
if (printOutput) {
const output = runner.getOutput();
if (output.length) {
consola.log('Program output: ');
output.forEach((value) => consola.log(value.toString()));
} else {
consola.log('Output segment is empty');
}
}
} catch (err) {
consola.fail(`Execution failed`);
throw err;
}
});

program.addHelpText(
'beforeAll',
'\nGitHub: https://github.com/kkrt-labs/cairo-vm-ts\nTelegram: https://t.me/cairovmts\n'
);
program.showHelpAfterError();
program.parse();
3 changes: 3 additions & 0 deletions src/errors/cairoRunner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class CairoRunnerError extends Error {}

export class EmptyRelocatedMemory extends CairoRunnerError {}
2 changes: 1 addition & 1 deletion src/errors/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ export class SegmentOutOfBounds extends MemoryError {
}

/** Instruction must be a Field Element */
export class InstructionError extends MemoryError {}
export class InvalidInstruction extends MemoryError {}
41 changes: 41 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export * as PrimitiveErrors from 'errors/primitives';
export * as MemoryErrors from 'errors/memory';
export * as InstructionErrors from 'errors/instruction';
export * as VirtualMachineErrors from 'errors/virtualMachine';
export * as BuiltinErrors from 'errors/builtins';
export * as CairoRunnerErrors from 'errors/cairoRunner';

export { Felt } from 'primitives/felt';
export { Relocatable } from 'primitives/relocatable';
export { SegmentValue, isFelt, isRelocatable } from 'primitives/segmentValue';

export { Memory } from 'memory/memory';
export {
Instruction,
Register,
Op1Src,
ResLogic,
Opcode,
PcUpdate,
ApUpdate,
FpUpdate,
} from 'vm/instruction';
export {
VirtualMachine,
TraceEntry,
RelocatedMemory,
RelocatedTraceEntry,
} from 'vm/virtualMachine';
export { parseProgram, Program, Identifier } from 'vm/program';

export { BuiltinHandler, getBuiltin } from 'builtins/builtin';
export { outputHandler } from 'builtins/output';
export { pedersenHandler } from 'builtins/pedersen';
export { rangeCheckHandler } from 'builtins/rangeCheck';
export { ecdsaHandler, EcdsaSegment, EcdsaSignature } from 'builtins/ecdsa';
export { bitwiseHandler } from 'builtins/bitwise';
export { ecOpHandler } from 'builtins/ecop';
export { keccakHandler } from 'builtins/keccak';
export { poseidonHandler } from 'builtins/poseidon';

export { CairoRunner, RunOptions } from 'runners/cairoRunner';
Loading

0 comments on commit c713512

Please sign in to comment.