From c713512ffbfbb3b6dde36b8d4625cf6fe81194f7 Mon Sep 17 00:00:00 2001 From: Malatrax <71888134+zmalatrax@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:51:38 +0200 Subject: [PATCH] feat: add basic cli (#85) * 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 --- .github/workflows/trunk-check.yaml | 8 +- .gitignore | 1 + README.md | 55 ++++++++++-- bun.lockb | Bin 4992 -> 26646 bytes package.json | 38 ++++++-- src/cli.ts | 137 +++++++++++++++++++++++++++++ src/errors/cairoRunner.ts | 3 + src/errors/memory.ts | 2 +- src/index.ts | 41 +++++++++ src/runners/cairoRunner.test.ts | 89 ++++--------------- src/runners/cairoRunner.ts | 16 ++-- src/vm/virtualMachine.test.ts | 29 +++--- src/vm/virtualMachine.ts | 12 +-- tsconfig.json | 39 ++++---- tsconfig.types.json | 21 +++++ 15 files changed, 357 insertions(+), 134 deletions(-) create mode 100755 src/cli.ts create mode 100644 src/errors/cairoRunner.ts create mode 100644 tsconfig.types.json diff --git a/.github/workflows/trunk-check.yaml b/.github/workflows/trunk-check.yaml index d3f0ec10..1db3fc33 100644 --- a/.github/workflows/trunk-check.yaml +++ b/.github/workflows/trunk-check.yaml @@ -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 diff --git a/.gitignore b/.gitignore index 5da719c6..92f900e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules +dist cairo_programs/**/*.json diff --git a/README.md b/README.md index 0a969407..f1219e81 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -# TypeScript Cairo VM +# Cairo VM Typescript
TypeScript Cairo VM
Cairo VM TypeScript
)TywxEd$HXOgn<4Ljh)*&ovCucep8mwD zBsNO?*%3cwfC07P01=OO#FH7-4s~B3KJkdJGf4@IDe;O&yq!^OlpPR1dBp!2g`spn zJmV3MXHS(L|)$=ufVC$t*^E@ev64Fm#Px?pg>!|W?BH?XLSCr9ou*OalE$)eNkieoMI0(U^;tDjn3|%B4lprK z>F#EmI*qWS;UW=N2p$kU%QWNN{ainA;AR2q#<&Davx_oO9lrgz`Vqnn2-hMun)RLJ zFBi*%iv&~yk)_#>6vFga@lcMlmXr1ZYs+=Z4sDn8Bi5f11J}W@1~dBE5{# b2_2mj#fg%HL6ci&E z)FZMF<@*G44LA|dPm#R{Qmz3!ND6~Ox#0~c@k&SE*wEO-$k5Pqys@#Vm61_Ec(_0` z$iP6z^#@;%!kPM^f{;LwK40i>0E;C97XGqeT?N+<>*oym;r`LSfHmNXLeTrDKH#OW ziWg@ LMfg27KY0D%b$Rz7It007l+{e}Dpa7W1r;05~zvN=H_0hb*_%{1ux zlDb)-I)pQ&u+wTmBeXM?c>RTK1q&mf` @z}(mjv_S`wHo^5PCxAsQLJ{dBWOKMIZj=B_jK>d^BKr?Df$oPK z7PUH|NZG;cP!89ZMKvakvq?pyY?1+b$1X~@iH6Yzc*8MRD$ZtrwC9P~Y>vo)6U>X? z*$aaF;hGV!g(5Cn=r7GYf2<1Z4KNl=dMKK7!J4!Ta7qFewU9<6Tx4*`vS#pn!vo-2 z8$>_1hq)NENzY<&0!4T(VBts+fcVUwGC`xpVyrwv$Zo9LaAGfBSv8zvO(_G^P!Hs9 zI0lo&`# 0?oixl}2(`ztbDH2OFJ?a6OGCelj zrA0h;C}nX{*k~pI-JqSpjNbgcfZL=QBxsTW(;80U(nsLeMr97@<}Sn^Z`B~fk6CbG zpe3Mj3i%=tOC(^=fN91b7J+b|;z_eoS}h#pMhqBy^A 6Hl~Rc22I&VmQ5j7oZ)70_%}u{e z>d^=(T#F_%YC#2l&jCyh8^>CT!X*290FxX6N-||jMM)OP6pk#I#ShNSnD`G1XY8zG z4NWabDCJ95+VE5uAobqYq#iJd0l))K{mqF%U(91Ks8u}$ZEpOVJO|LBl0}gez5P<) z7C;*wG5+;n0x~ptP+_%bAb$M}1bn|P1p9yuXw F4Wf zoWq*CtbbTOL(@OxP CQ8ki(Nr1WlDI3HmF_@PHLarY#3i%|D?NS-Vb%0NN0MjepIwZp-*#nXy zkfa9SB}bqqW7va023Txy;AxN*!uO2`=2FuPwQ3hd2n2i~$`2Ua1z?G0h6M9MgJ7Wt z|3rb5)|x?LFQjvWbj>gH8YKn7#uExlS+W!foAw#-O*3GshfS~MZp`BCEdYxT8t+Sf z5uu!Lcrs+c==w^LqDejAX>v|ka&y-MbtU2cf%xGV|H%m)S@FP1X}O6F0=UUJn5snk z;#)Lz*Ofxn^zKaUyrEr5K}fdppkc`o^<2~3 l&<>DW-<`kl0Hql$)%j z^pG(i8q^ljOVL9~V1;yf2tnxvLD+*-L}4xTC zW@JberC?h*fSwk zGAh|W#0@dG+iaVAV=<514cIkB?+@xH;*0gAXS3^e7C-%x#birAA1xSYN?#*u-!#Tz z{oU;{DZ_6j@|TE4#Qfh-J FZW0WU>h{ |G`|eXs zaNyzOmEf2OlA4YB#HQHhvZ0Pc@F1l#H;id^p K1;)0?hc`u^*zkk(=zJpl!WNJPP^pEUR3Fdb(6z3TcW}6daGk>v!e SX&FNlzUio!VkT;$;8G@BP0A;Es77w%bC221aaAuvv_d zM5cwU_9(nUcF`UZbTCmGg<-1^hMXZmgy+sE`16h6afO65IORGH4tG0rdc%mpsb0T@ z8!js)%%;P1b-=;c>bEtcQmAYyi~blHaVmdV2`Vx||L$V&*JKyc70!y>IqD8|Ar(E0 z14b4nX<;QbmYS?Kp0cN)(+lJjqKuoIj?zvmDGZsgvZt6-P*9uaQKwU#F}=|2kzA6C zF?F7A%(Ns=R41--q1h>wOITbP%+%(F)jd$>mGidNqpipK{uX)CDY+?9-3jlb`X$r{)4>`!G(}7nb)UrUz~iO53-?ff zm3XCc5{yPnh6dbLvhhr~6$!#}#9@Adox{Hni~BwELT|?EtRnfC1%vfL_*!p;4f_wk V?dH6xsursNX{~Kj=MJ9{z5^_e(xU(X diff --git a/package.json b/package.json index 656c5118..de7b4a33 100644 --- a/package.json +++ b/package.json @@ -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 ", "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", @@ -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" } } diff --git a/src/cli.ts b/src/cli.ts new file mode 100755 index 00000000..f3db9ec1 --- /dev/null +++ b/src/cli.ts @@ -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( + ' ', + '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 ', + 'start address of the relocated memory\nStarkWare verifier expects offset to be 1' + ) + .default(0, '0') + .argParser(parseInt) + ) + .option( + '--export-trace ', + 'export the trace, little-endian encoded' + ) + .option( + '--export-memory ', + '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 ', '--export-memory ' 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(); diff --git a/src/errors/cairoRunner.ts b/src/errors/cairoRunner.ts new file mode 100644 index 00000000..ce32834d --- /dev/null +++ b/src/errors/cairoRunner.ts @@ -0,0 +1,3 @@ +class CairoRunnerError extends Error {} + +export class EmptyRelocatedMemory extends CairoRunnerError {} diff --git a/src/errors/memory.ts b/src/errors/memory.ts index 86fbc768..68901090 100644 --- a/src/errors/memory.ts +++ b/src/errors/memory.ts @@ -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 {} diff --git a/src/index.ts b/src/index.ts index e69de29b..af945b46 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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'; diff --git a/src/runners/cairoRunner.test.ts b/src/runners/cairoRunner.test.ts index 7e516d43..55267768 100644 --- a/src/runners/cairoRunner.test.ts +++ b/src/runners/cairoRunner.test.ts @@ -109,10 +109,7 @@ describe('cairoRunner', () => { describe('run', () => { test('should return the value of the 10th fibonacci number', () => { const runner = new CairoRunner(FIBONACCI_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 0, - }; + const config: RunOptions = { relocate: true, offset: 0 }; runner.run(config); const executionSize = runner.vm.memory.getSegmentSize(1); const executionEnd = runner.executionBase.add(executionSize); @@ -126,10 +123,7 @@ describe('cairoRunner', () => { */ test('should export encoded trace', () => { const runner = new CairoRunner(FIBONACCI_PROGRAM); - const config: RunOptions = { - relocate: false, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: false, offset: 1 }; runner.run(config); const trace_filename = 'fibonacci_trace_ts.bin'; const trace_path = path.join(tmpDir, trace_filename); @@ -143,14 +137,11 @@ describe('cairoRunner', () => { test('should export encoded memory', () => { const runner = new CairoRunner(FIBONACCI_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const memoryFilename = 'fibonacci_memory_ts.bin'; const memoryPath = path.join(tmpDir, memoryFilename); - runner.exportMemory(memoryPath, config.relocateOffset); + runner.exportMemory(memoryPath, config.offset); expect(() => fs.access(memoryPath, (err) => { if (err) throw err; @@ -163,10 +154,7 @@ describe('cairoRunner', () => { describe('bitwise', () => { test('should compute bitwise 12 & 10', () => { const runner = new CairoRunner(BITWISE_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const executionSize = runner.vm.memory.getSegmentSize(1); const executionEnd = runner.executionBase.add(executionSize); @@ -177,10 +165,7 @@ describe('cairoRunner', () => { describe('ec_op', () => { test('should properly compute R = P + 34Q', () => { const runner = new CairoRunner(EC_OP_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const expectedRx = new Felt( @@ -200,10 +185,7 @@ describe('cairoRunner', () => { describe('pedersen', () => { test('should properly compute Pedersen hashes of (0, 0), (0, 1), (1, 0) and (54, 1249832432) tuples', () => { const runner = new CairoRunner(PEDERSEN_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const expectedHashes = [ @@ -232,10 +214,7 @@ describe('cairoRunner', () => { describe('poseidon', () => { test('should properly compute Poseidon states from initial states (1, 2, 3) and (13, 40, 36)', () => { const runner = new CairoRunner(POSEIDON_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const expectedStates = [ @@ -279,10 +258,7 @@ describe('cairoRunner', () => { describe('keccak', () => { test('Should properly compute state from input state KeccakBuiltinState(0, 0, 0, 0, 0, 0, 0, 0)', () => { const runner = new CairoRunner(KECCAK_SEED_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const expectedState = [ @@ -308,10 +284,7 @@ describe('cairoRunner', () => { test('Should properly compute state from input state KeccakBuiltinState(1, 2, 3, 4, 5, 6, 7, 8)', () => { const runner = new CairoRunner(KECCAK_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const expectedState = [ @@ -339,10 +312,7 @@ describe('cairoRunner', () => { describe('output', () => { test('Should properly store the jmp dest value in the output segment', () => { const runner = new CairoRunner(JMP_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const output = runner.getOutput(); expect(output.length).toEqual(1); @@ -351,10 +321,7 @@ describe('cairoRunner', () => { test('Should properly write the result of bitwise 1 & 2 to output segment', () => { const runner = new CairoRunner(BITWISE_OUTPUT_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const output = runner.getOutput(); expect(output.length).toEqual(1); @@ -365,10 +332,7 @@ describe('cairoRunner', () => { describe('range_check', () => { test('should properly write 2 ** 128 - 1 to the range check segment', () => { const runner = new CairoRunner(RANGE_CHECK_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const executionSize = runner.vm.memory.getSegmentSize(1); const executionEnd = runner.executionBase.add(executionSize); @@ -379,10 +343,7 @@ describe('cairoRunner', () => { test('should crash the VM when trying to assert -1 to the range check segment', () => { const runner = new CairoRunner(BAD_RANGE_CHECK_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; expect(() => runner.run(config)).toThrow(new RangeCheckOutOfBounds()); }); }); @@ -390,10 +351,7 @@ describe('cairoRunner', () => { describe('range_check96', () => { test('should properly write 2 ** 96 - 1 to the range check segment', () => { const runner = new CairoRunner(RANGE_CHECK96_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const executionSize = runner.vm.memory.getSegmentSize(1); const executionEnd = runner.executionBase.add(executionSize); @@ -404,10 +362,7 @@ describe('cairoRunner', () => { test('should crash the VM when trying to assert 2 ** 96 to the range check segment', () => { const runner = new CairoRunner(BAD_RANGE_CHECK96_PROGRAM); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; expect(() => runner.run(config)).toThrow(new RangeCheckOutOfBounds()); }); }); @@ -421,13 +376,10 @@ describe('cairoRunner', () => { const program = parseProgram(fs.readFileSync(programPath, 'utf8')); const runner = new CairoRunner(program); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const tsMemoryPath = path.join(tmpDir, 'memory_ts.bin'); - runner.exportMemory(tsMemoryPath, config.relocateOffset); + runner.exportMemory(tsMemoryPath, config.offset); const tsMemory = fs.readFileSync(tsMemoryPath); @@ -442,10 +394,7 @@ describe('cairoRunner', () => { const program = parseProgram(fs.readFileSync(programPath, 'utf8')); const runner = new CairoRunner(program); - const config: RunOptions = { - relocate: true, - relocateOffset: 1, - }; + const config: RunOptions = { relocate: true, offset: 1 }; runner.run(config); const tsTracePath = path.join(tmpDir, 'trace_ts.bin'); runner.exportTrace(tsTracePath); diff --git a/src/runners/cairoRunner.ts b/src/runners/cairoRunner.ts index 4273a109..480b3802 100644 --- a/src/runners/cairoRunner.ts +++ b/src/runners/cairoRunner.ts @@ -1,18 +1,20 @@ import * as fs from 'fs'; +import { EmptyRelocatedMemory } from 'errors/cairoRunner'; + import { Relocatable } from 'primitives/relocatable'; -import { VirtualMachine } from 'vm/virtualMachine'; import { Program } from 'vm/program'; +import { VirtualMachine } from 'vm/virtualMachine'; import { getBuiltin } from 'builtins/builtin'; /** * Configuration of the run * - relocate: Flag to relocate the memory and the trace - * - relocateOffset: Start address of the relocated memory + * - offset: Start address of the relocated memory */ export type RunOptions = { relocate: boolean; - relocateOffset: number; + offset: number; }; export class CairoRunner { @@ -24,7 +26,7 @@ export class CairoRunner { static readonly defaultRunOptions: RunOptions = { relocate: false, - relocateOffset: 0, + offset: 0, }; constructor(program: Program) { @@ -58,8 +60,8 @@ export class CairoRunner { while (!this.vm.pc.eq(this.finalPc)) { this.vm.step(); } - const { relocate, relocateOffset } = config; - if (relocate) this.vm.relocate(relocateOffset); + const { relocate, offset } = config; + if (relocate) this.vm.relocate(offset); } /** @@ -91,6 +93,8 @@ export class CairoRunner { * @dev DataView must be used to enforce little-endianness */ exportMemory(filename: string = 'encoded_memory', offset: number = 0) { + if (!this.vm.relocatedMemory.length) throw new EmptyRelocatedMemory(); + const buffer = new ArrayBuffer(this.vm.relocatedMemory.length * 5 * 8); const view = new DataView(buffer); diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index 9f15e608..a9cdd6a5 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -16,6 +16,7 @@ import { PcUpdate, ApUpdate, FpUpdate, + Op1Src, } from './instruction'; import { VirtualMachine } from './virtualMachine'; @@ -26,7 +27,7 @@ const instructions = { 3, Register.Fp, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Add, PcUpdate.Regular, ApUpdate.Ap, @@ -39,7 +40,7 @@ const instructions = { 3, Register.Fp, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Add, PcUpdate.Regular, ApUpdate.Ap, @@ -52,7 +53,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.Regular, ApUpdate.Ap, @@ -65,7 +66,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Pc, + Op1Src.Pc, ResLogic.Unused, PcUpdate.Regular, ApUpdate.Ap, @@ -78,7 +79,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.Jump, ApUpdate.Ap, @@ -91,7 +92,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.JumpRel, ApUpdate.Ap, @@ -104,7 +105,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.JumpRel, ApUpdate.Add2, @@ -117,7 +118,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.Jnz, ApUpdate.Ap, @@ -130,7 +131,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Pc, + Op1Src.Pc, ResLogic.Unused, PcUpdate.Jnz, ApUpdate.Ap, @@ -143,7 +144,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.Regular, ApUpdate.Ap, @@ -156,7 +157,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.Regular, ApUpdate.Ap, @@ -169,7 +170,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.Regular, ApUpdate.Add2, @@ -182,7 +183,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.Regular, ApUpdate.Add1, @@ -195,7 +196,7 @@ const instructions = { 0, Register.Ap, Register.Ap, - Register.Ap, + Op1Src.Ap, ResLogic.Unused, PcUpdate.Regular, ApUpdate.AddRes, diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 6d4b2cb7..669bca12 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -7,7 +7,7 @@ import { UndefinedInstruction, InvalidOp0, } from 'errors/virtualMachine'; -import { InstructionError } from 'errors/memory'; +import { InvalidInstruction } from 'errors/memory'; import { Relocatable } from 'primitives/relocatable'; import { Felt } from 'primitives/felt'; @@ -24,19 +24,19 @@ import { ResLogic, } from './instruction'; -type TraceEntry = { +export type TraceEntry = { pc: Relocatable; ap: Relocatable; fp: Relocatable; }; -type RelocatedTraceEntry = { +export type RelocatedTraceEntry = { pc: Felt; ap: Felt; fp: Felt; }; -type RelocatedMemory = { +export type RelocatedMemory = { address: number; value: Felt; }; @@ -75,7 +75,7 @@ export class VirtualMachine { } if (!isFelt(maybeEncodedInstruction)) { - throw new InstructionError(); + throw new InvalidInstruction(); } const encodedInstruction = maybeEncodedInstruction.toBigInt(); @@ -93,7 +93,7 @@ export class VirtualMachine { * for more details */ runInstruction(instruction: Instruction): void { - const { op0, op1, res, dst } = this.computeStepValues(instruction); + const { op1, res, dst } = this.computeStepValues(instruction); this.trace.push({ pc: this.pc, ap: this.ap, fp: this.fp }); diff --git a/tsconfig.json b/tsconfig.json index eb0acfd8..384c831a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,29 +1,28 @@ { "compilerOptions": { - // add Bun type definitions - "types": ["bun-types"], - - // enable latest features + "outDir": "./dist", + "baseUrl": ".", + "paths": { + "*": ["./src/*"] + }, "lib": ["esnext"], "module": "esnext", "target": "esnext", - - "baseUrl": "./src", - - // if TS 5.x+ - "moduleResolution": "bundler", - "noEmit": true, - "allowImportingTsExtensions": true, + "moduleResolution": "node", "moduleDetection": "force", - // if TS 4.x or earlier - // "moduleResolution": "nodenext", - - "allowJs": false, // disallow importing `.js` from `.ts` - "esModuleInterop": true, // allow default imports for CommonJS modules - - // best practices + "allowImportingTsExtensions": true, + "noEmit": true, "strict": true, + "allowJs": false, + "skipLibCheck": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noImplicitReturns": true, + "noUnusedParameters": true, "forceConsistentCasingInFileNames": true, - "skipLibCheck": true - } + "types": ["bun-types"], + "esModuleInterop": true // allow default imports for CommonJS modules + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] } diff --git a/tsconfig.types.json b/tsconfig.types.json new file mode 100644 index 00000000..1b489557 --- /dev/null +++ b/tsconfig.types.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "emitDeclarationOnly": true, + "declaration": true, + "outDir": "./dist/types", + "rootDir": "./src", + "plugins": [ + { + "transform": "typescript-transform-paths" + }, + { + "transform": "typescript-transform-paths", + "afterDeclarations": true + } + ] + }, + "include": ["./src/**/*.ts"], + "exclude": ["node_modules", "./src/**/*.test.ts"] +}