From 5d58b5e5380e59c4c5c0afa898a9183a5c8328fc Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 16 Jul 2024 15:37:59 +0200 Subject: [PATCH 01/78] feat: add Cairo program with array --- cairo_programs/cairo/hints/array_append.cairo | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 cairo_programs/cairo/hints/array_append.cairo diff --git a/cairo_programs/cairo/hints/array_append.cairo b/cairo_programs/cairo/hints/array_append.cairo new file mode 100644 index 00000000..5fe852c8 --- /dev/null +++ b/cairo_programs/cairo/hints/array_append.cairo @@ -0,0 +1,6 @@ +fn main() { + let mut a = ArrayTrait::new(); + a.append(0); + a.append(1); + a.append(2); +} From 9020816be3438a5bf8ebbf15eb7d4bd7c7be1dc7 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 16 Jul 2024 15:38:50 +0200 Subject: [PATCH 02/78] feat: add segment_arena builtin --- src/builtins/builtin.ts | 4 +++- src/builtins/segmentArena.ts | 3 +++ src/runners/cairoRunner.ts | 19 ++++++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 src/builtins/segmentArena.ts diff --git a/src/builtins/builtin.ts b/src/builtins/builtin.ts index d9374643..e881df46 100644 --- a/src/builtins/builtin.ts +++ b/src/builtins/builtin.ts @@ -7,6 +7,7 @@ import { poseidonHandler } from './poseidon'; import { keccakHandler } from './keccak'; import { outputHandler } from './output'; import { rangeCheckHandler } from './rangeCheck'; +import { segmentArenaHandler } from './segmentArena'; /** Proxy handler to abstract validation & deduction rules off the VM */ export type BuiltinHandler = ProxyHandler>; @@ -20,7 +21,7 @@ export type BuiltinHandler = ProxyHandler>; * - Keccak: Builtin for keccak hash family * - Pedersen: Builtin for pedersen hash family * - Poseidon: Builtin for poseidon hash family - * - Segment Arena: Unknown usage, must investigate + * - Segment Arena: Builtin to manage the dictionnaries * - Output: Output builtin */ const BUILTIN_HANDLER: { @@ -35,6 +36,7 @@ const BUILTIN_HANDLER: { keccak: keccakHandler, range_check: rangeCheckHandler(128n), range_check96: rangeCheckHandler(96n), + segment_arena: segmentArenaHandler, }; /** Getter of the object `BUILTIN_HANDLER` */ diff --git a/src/builtins/segmentArena.ts b/src/builtins/segmentArena.ts new file mode 100644 index 00000000..e4b4b687 --- /dev/null +++ b/src/builtins/segmentArena.ts @@ -0,0 +1,3 @@ +import { BuiltinHandler } from './builtin'; + +export const segmentArenaHandler: BuiltinHandler = {}; diff --git a/src/runners/cairoRunner.ts b/src/runners/cairoRunner.ts index a7b7bde1..a916df73 100644 --- a/src/runners/cairoRunner.ts +++ b/src/runners/cairoRunner.ts @@ -65,9 +65,22 @@ export class CairoRunner { throw new InvalidBuiltins(builtins, this.layout.builtins, layoutName); this.builtins = builtins; - const builtin_stack = builtins - .map(getBuiltin) - .map((builtin) => this.vm.memory.addSegment(builtin)); + const builtin_stack = builtins.map((builtin) => { + const handler = getBuiltin(builtin); + if (builtin === 'segment_arena') { + const info = [ + this.vm.memory.addSegment(handler), + new Felt(0n), + new Felt(0n), + ]; + const base = this.vm.memory.addSegment(handler); + info.map((value, offset) => + this.vm.memory.assertEq(base.add(offset), value) + ); + return base.add(info.length); + } + return this.vm.memory.addSegment(handler); + }); const returnFp = this.vm.memory.addSegment(); this.finalPc = this.vm.memory.addSegment(); const stack = [...builtin_stack, returnFp, this.finalPc]; From d345e14872d70a6c93cfdf94e9ecb508d7f8ebd2 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 16 Jul 2024 15:39:09 +0200 Subject: [PATCH 03/78] feat: add ScopeManager --- src/errors/scopeManager.ts | 13 ++++++++ src/hints/scopeManager.test.ts | 55 ++++++++++++++++++++++++++++++++++ src/hints/scopeManager.ts | 51 +++++++++++++++++++++++++++++++ src/vm/virtualMachine.ts | 4 +++ 4 files changed, 123 insertions(+) create mode 100644 src/errors/scopeManager.ts create mode 100644 src/hints/scopeManager.test.ts create mode 100644 src/hints/scopeManager.ts diff --git a/src/errors/scopeManager.ts b/src/errors/scopeManager.ts new file mode 100644 index 00000000..df7796e0 --- /dev/null +++ b/src/errors/scopeManager.ts @@ -0,0 +1,13 @@ +class ScopeManagerError extends Error {} + +export class CannotExitMainScope extends ScopeManagerError { + constructor() { + super('Cannot exit the main scope'); + } +} + +export class VariableNotInScope extends ScopeManagerError { + constructor(name: string) { + super(`Variable ${name} is not in scope`); + } +} diff --git a/src/hints/scopeManager.test.ts b/src/hints/scopeManager.test.ts new file mode 100644 index 00000000..2c2c7468 --- /dev/null +++ b/src/hints/scopeManager.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, test } from 'bun:test'; +import { ScopeManager } from './scopeManager'; +import { CannotExitMainScope, VariableNotInScope } from 'errors/scopeManager'; +import { Felt } from 'primitives/felt'; + +describe('ScopeManager', () => { + test('constructor', () => { + const scopeManager = new ScopeManager(); + expect(scopeManager.data.length).toEqual(1); + }); + + test('should properly enter a new scope', () => { + const scopeManager = new ScopeManager(); + scopeManager.enterScope({}); + expect(scopeManager.data.length).toEqual(2); + }); + + test('should properly delete new scopes', () => { + const scopeManager = new ScopeManager(); + scopeManager.enterScope({}); + scopeManager.enterScope({}); + scopeManager.exitScope(); + expect(scopeManager.data.length).toEqual(2); + }); + + test.each([10, 'a', 4n, new Felt(3n), [1, 2, 3], { a: 3, b: [] }])( + 'should properly set variables', + (value: any) => { + const scopeManager = new ScopeManager(); + scopeManager.set('value', value); + expect(scopeManager.get('value')).toEqual(value); + } + ); + + test.each([10, 'a', 4n, new Felt(3n), [1, 2, 3], { a: 3, b: [] }])( + 'should properly delete a defined variable', + (value) => { + const scopeManager = new ScopeManager(); + expect(() => scopeManager.get('value')).toThrow( + new VariableNotInScope('value') + ); + scopeManager.set('value', value); + expect(() => scopeManager.get('value')).not.toThrow(); + scopeManager.delete('value'); + expect(() => scopeManager.get('value')).toThrow( + new VariableNotInScope('value') + ); + } + ); + + test('should throw if trying to delete main scope', () => { + const scopeManager = new ScopeManager(); + expect(() => scopeManager.exitScope()).toThrow(new CannotExitMainScope()); + }); +}); diff --git a/src/hints/scopeManager.ts b/src/hints/scopeManager.ts new file mode 100644 index 00000000..a7863532 --- /dev/null +++ b/src/hints/scopeManager.ts @@ -0,0 +1,51 @@ +import { VariableNotInScope, CannotExitMainScope } from 'errors/scopeManager'; + +/** + * A dictionnary mapping a variable name to its value, + * which can be anything + */ +export type Scope = { + [key: string]: any; +}; + +/** + * A stack of Scope + * Scopes are used to share variables across hints + * Only the latest scope is available + * There is always one scope in the stack, the main scope + */ +export class ScopeManager { + public data: Scope[]; + + constructor() { + this.data = [{}]; + } + + /** Add a new scope to the stack */ + enterScope(newScope: Scope) { + this.data.push(newScope); + } + + /** Pop the stack */ + exitScope() { + if (this.data.length === 1) throw new CannotExitMainScope(); + this.data.pop(); + } + + /** Return the value of variable `name` in the latest scope */ + get(name: string) { + const variable = this.data[this.data.length - 1][name]; + if (variable === undefined) throw new VariableNotInScope(name); + return variable; + } + + /** Set the variable `name` to `value` in the latest scope */ + set(name: string, value: any) { + this.data[this.data.length - 1][name] = value; + } + + /** Delete the variable `name` from the latest scope */ + delete(name: string) { + delete this.data[this.data.length - 1][name]; + } +} diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index b665c3d6..5b513548 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -39,6 +39,7 @@ import { Register, ResLogic, } from './instruction'; +import { ScopeManager } from 'hints/scopeManager'; export type TraceEntry = { pc: Relocatable; @@ -63,6 +64,7 @@ export class VirtualMachine { pc: Relocatable; ap: Relocatable; fp: Relocatable; + scopeManager: ScopeManager; trace: TraceEntry[]; relocatedMemory: RelocatedMemory[]; relocatedTrace: RelocatedTraceEntry[]; @@ -90,6 +92,8 @@ export class VirtualMachine { this.pc = new Relocatable(0, 0); this.ap = new Relocatable(1, 0); this.fp = new Relocatable(1, 0); + + this.scopeManager = new ScopeManager(); } /** From 78054c23196f9b3a1c7fc9e8fdc1ec3ea7af2a2f Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 16 Jul 2024 18:06:49 +0200 Subject: [PATCH 04/78] feat: add DictManager --- src/errors/virtualMachine.ts | 17 +++++++++++++ src/vm/virtualMachine.test.ts | 46 +++++++++++++++++++++++++++++++++-- src/vm/virtualMachine.ts | 30 +++++++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/src/errors/virtualMachine.ts b/src/errors/virtualMachine.ts index f082aec0..09c41528 100644 --- a/src/errors/virtualMachine.ts +++ b/src/errors/virtualMachine.ts @@ -1,3 +1,4 @@ +import { Felt } from 'primitives/felt'; import { Relocatable } from 'primitives/relocatable'; import { SegmentValue } from 'primitives/segmentValue'; @@ -57,3 +58,19 @@ export class UndefinedOp1 extends VirtualMachineError { super('op1 is undefined'); } } + +/** Cannot find Dictionnary at `address */ +export class DictNotFound extends VirtualMachineError { + constructor(address: Relocatable) { + super(`Cannot found any Dictionnary at address ${address.toString()}`); + } +} + +/** Cannot find value at `key` in Dictionnary at `address */ +export class DictValueNotFound extends VirtualMachineError { + constructor(address: Relocatable, key: Felt) { + super( + `Cannot found value at ${key.toString()} from Dictionnary at address ${address.toString()}` + ); + } +} diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index 99652fe3..dc113512 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -1,7 +1,11 @@ import { test, expect, describe, spyOn } from 'bun:test'; import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; -import { UnusedRes } from 'errors/virtualMachine'; +import { + DictNotFound, + DictValueNotFound, + UnusedRes, +} from 'errors/virtualMachine'; import { Felt } from 'primitives/felt'; import { Relocatable } from 'primitives/relocatable'; @@ -15,7 +19,7 @@ import { FpUpdate, Op1Src, } from './instruction'; -import { VirtualMachine } from './virtualMachine'; +import { Dictionnary, VirtualMachine } from './virtualMachine'; const instructions = { InvalidAssertEq: new Instruction( @@ -519,4 +523,42 @@ describe('VirtualMachine', () => { expect(logSpy.mock.results[0].value).toEqual(expectedStr); }); }); + + describe('Dictionnary', () => { + test('should properly initialize the dict manager', () => { + const vm = new VirtualMachine(); + expect(vm.dictManager.size).toEqual(0); + }); + + test('should properly create a new dictionnary', () => { + const vm = new VirtualMachine(); + const address = vm.newDict(); + expect(address).toEqual(new Relocatable(0, 0)); + expect(vm.getDict(address)).toEqual(new Dictionnary()); + }); + + test('should properly set and get value of a dictionnary', () => { + const vm = new VirtualMachine(); + const address = vm.newDict(); + const key = new Felt(12n); + const value = new Felt(5n); + vm.setDictValue(address, key, value); + expect(vm.getDictValue(address, key)).toEqual(value); + }); + + test('should throw DictNotFound when accessing a non-existing dictionnary', () => { + const vm = new VirtualMachine(); + const address = new Relocatable(2, 3); + expect(() => vm.getDict(address)).toThrowError(new DictNotFound(address)); + }); + + test('should throw DictValueNotFound when accessing a non-existing key in a dictionnary', () => { + const vm = new VirtualMachine(); + const address = vm.newDict(); + const key = new Felt(0n); + expect(() => vm.getDictValue(address, key)).toThrowError( + new DictValueNotFound(address, key) + ); + }); + }); }); diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 5b513548..2f15b3a4 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -8,6 +8,8 @@ import { UndefinedOp0, InvalidCallOp0Value, UndefinedOp1, + DictNotFound, + DictValueNotFound, } from 'errors/virtualMachine'; import { InvalidCellRefRegister, UnknownHint } from 'errors/hints'; @@ -58,12 +60,15 @@ export type RelocatedMemory = { value: Felt; }; +export class Dictionnary extends Map {} + export class VirtualMachine { private currentStep: bigint; memory: Memory; pc: Relocatable; ap: Relocatable; fp: Relocatable; + dictManager: Map; scopeManager: ScopeManager; trace: TraceEntry[]; relocatedMemory: RelocatedMemory[]; @@ -94,6 +99,7 @@ export class VirtualMachine { this.fp = new Relocatable(1, 0); this.scopeManager = new ScopeManager(); + this.dictManager = new Map(); } /** @@ -598,4 +604,28 @@ export class VirtualMachine { } } } + + newDict(): Relocatable { + const dictAddr = this.memory.addSegment(); + this.dictManager.set(dictAddr.segmentId, new Dictionnary()); + return dictAddr; + } + + getDict(address: Relocatable): Dictionnary { + const dict = this.dictManager.get(address.segmentId); + if (!dict) throw new DictNotFound(address); + return dict; + } + + getDictValue(address: Relocatable, key: Felt): SegmentValue { + const dict = this.getDict(address); + const value = dict.get(key); + if (!value) throw new DictValueNotFound(address, key); + return value; + } + + setDictValue(address: Relocatable, key: Felt, value: SegmentValue) { + const dict = this.getDict(address); + dict.set(key, value); + } } From 536d01c02cac6663bee03cc4e104724f62c206e5 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 16 Jul 2024 19:12:50 +0200 Subject: [PATCH 05/78] feat: add SquashedDictManager --- src/errors/squashedDict.ts | 25 +++++++++++++++ src/vm/squashedDict.test.ts | 57 +++++++++++++++++++++++++++++++++++ src/vm/squashedDict.ts | 53 ++++++++++++++++++++++++++++++++ src/vm/virtualMachine.test.ts | 4 +-- src/vm/virtualMachine.ts | 3 ++ 5 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 src/errors/squashedDict.ts create mode 100644 src/vm/squashedDict.test.ts create mode 100644 src/vm/squashedDict.ts diff --git a/src/errors/squashedDict.ts b/src/errors/squashedDict.ts new file mode 100644 index 00000000..5691fc8c --- /dev/null +++ b/src/errors/squashedDict.ts @@ -0,0 +1,25 @@ +import { Felt } from 'primitives/felt'; + +class SquashedDictManagerError extends Error {} + +export class EmptyKeys extends SquashedDictManagerError { + constructor() { + super('There is no keys left in the squashed dictionnary.'); + } +} + +export class EmptyIndices extends SquashedDictManagerError { + constructor(key: Felt | undefined) { + super( + `There is no indices at key ${ + key ? key.toString() : key + } in the squashed dictionnary.` + ); + } +} + +export class EmptyIndex extends SquashedDictManagerError { + constructor() { + super('The last index of the squashed dictionnary is empty.'); + } +} diff --git a/src/vm/squashedDict.test.ts b/src/vm/squashedDict.test.ts new file mode 100644 index 00000000..9b706d7e --- /dev/null +++ b/src/vm/squashedDict.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, test } from 'bun:test'; +import { SquashedDictManager } from './squashedDict'; +import { Felt } from 'primitives/felt'; +import { EmptyKeys } from 'errors/squashedDict'; + +describe('squashed dictionnary', () => { + test('should properly initialize a SquashedDictManager', () => { + const squashedDictManager = new SquashedDictManager(); + expect(squashedDictManager.keys.length).toEqual(0); + expect(squashedDictManager.keyToIndices.size).toEqual(0); + }); + + test('should properly insert new index', () => { + const squashedDictManager = new SquashedDictManager(); + const key = new Felt(2n); + const indices = [new Felt(3n), new Felt(5n), new Felt(1n)]; + squashedDictManager.keys.push(key); + indices.map((index) => squashedDictManager.insert(key, index)); + expect(squashedDictManager.lastIndices()).toEqual(indices); + }); + + test('should properly get the last index', () => { + const squashedDictManager = new SquashedDictManager(); + const key = new Felt(2n); + const indices = [new Felt(3n), new Felt(5n), new Felt(1n)]; + squashedDictManager.keys.push(key); + indices.map((index) => squashedDictManager.insert(key, index)); + expect(squashedDictManager.lastIndex()).toEqual( + indices[indices.length - 1] + ); + }); + + test('should properly pop the last index', () => { + const squashedDictManager = new SquashedDictManager(); + const key = new Felt(2n); + const indices = [new Felt(3n), new Felt(5n), new Felt(1n)]; + squashedDictManager.keys.push(key); + indices.map((index) => squashedDictManager.insert(key, index)); + expect(squashedDictManager.popIndex()).toEqual(indices[indices.length - 1]); + expect(squashedDictManager.lastIndices()).toEqual( + indices.slice(0, indices.length - 1) + ); + }); + + test('should properly pop the last key', () => { + const squashedDictManager = new SquashedDictManager(); + const keys = [new Felt(2n), new Felt(5n), new Felt(7n)]; + keys.map((key) => squashedDictManager.keys.push(key)); + expect(squashedDictManager.popKey()).toEqual(keys[keys.length - 1]); + expect(squashedDictManager.keys).toEqual(keys.slice(0, keys.length - 1)); + }); + + test('should throw if there is no keys', () => { + const squashedDictManager = new SquashedDictManager(); + expect(() => squashedDictManager.lastKey()).toThrow(new EmptyKeys()); + }); +}); diff --git a/src/vm/squashedDict.ts b/src/vm/squashedDict.ts new file mode 100644 index 00000000..3d75061a --- /dev/null +++ b/src/vm/squashedDict.ts @@ -0,0 +1,53 @@ +import { EmptyIndex, EmptyIndices, EmptyKeys } from 'errors/squashedDict'; +import { Felt } from 'primitives/felt'; + +export class SquashedDictManager { + public keyToIndices: Map; + public keys: Felt[]; + + constructor() { + this.keyToIndices = new Map(); + this.keys = []; + } + + insert(key: Felt, index: Felt) { + const indices = this.keyToIndices.get(key); + if (!indices) { + this.keyToIndices.set(key, [index]); + } else { + indices.push(index); + } + } + + lastKey(): Felt { + const len = this.keys.length; + if (!len) throw new EmptyKeys(); + return this.keys[len - 1]; + } + + popKey(): Felt { + const key = this.keys.pop(); + if (!key) throw new EmptyKeys(); + return key; + } + + lastIndices(): Felt[] { + const key = this.lastKey(); + const indices = this.keyToIndices.get(key); + if (!indices) throw new EmptyIndices(key); + return indices; + } + + lastIndex(): Felt { + const indices = this.lastIndices(); + const len = indices.length; + if (!len) throw new EmptyIndex(); + return indices[len - 1]; + } + + popIndex(): Felt { + const index = this.lastIndices().pop(); + if (!index) throw new EmptyIndex(); + return index; + } +} diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index dc113512..9a01c8cd 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -549,14 +549,14 @@ describe('VirtualMachine', () => { test('should throw DictNotFound when accessing a non-existing dictionnary', () => { const vm = new VirtualMachine(); const address = new Relocatable(2, 3); - expect(() => vm.getDict(address)).toThrowError(new DictNotFound(address)); + expect(() => vm.getDict(address)).toThrow(new DictNotFound(address)); }); test('should throw DictValueNotFound when accessing a non-existing key in a dictionnary', () => { const vm = new VirtualMachine(); const address = vm.newDict(); const key = new Felt(0n); - expect(() => vm.getDictValue(address, key)).toThrowError( + expect(() => vm.getDictValue(address, key)).toThrow( new DictValueNotFound(address, key) ); }); diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 2f15b3a4..5f18f34f 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -42,6 +42,7 @@ import { ResLogic, } from './instruction'; import { ScopeManager } from 'hints/scopeManager'; +import { SquashedDictManager } from './squashedDict'; export type TraceEntry = { pc: Relocatable; @@ -69,6 +70,7 @@ export class VirtualMachine { ap: Relocatable; fp: Relocatable; dictManager: Map; + squashedDictManager: SquashedDictManager; scopeManager: ScopeManager; trace: TraceEntry[]; relocatedMemory: RelocatedMemory[]; @@ -100,6 +102,7 @@ export class VirtualMachine { this.scopeManager = new ScopeManager(); this.dictManager = new Map(); + this.squashedDictManager = new SquashedDictManager(); } /** From 56ab2f32136f46b490c38ccecb94d1fee94c310b Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 16 Jul 2024 19:14:21 +0200 Subject: [PATCH 06/78] feat: add dict Cairo programs --- cairo_programs/cairo/hints/alloc_felt_dict.cairo | 3 +++ cairo_programs/cairo/hints/dict_get.cairo | 5 +++++ cairo_programs/cairo/hints/dict_insert.cairo | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 cairo_programs/cairo/hints/alloc_felt_dict.cairo create mode 100644 cairo_programs/cairo/hints/dict_get.cairo create mode 100644 cairo_programs/cairo/hints/dict_insert.cairo diff --git a/cairo_programs/cairo/hints/alloc_felt_dict.cairo b/cairo_programs/cairo/hints/alloc_felt_dict.cairo new file mode 100644 index 00000000..c7436f8a --- /dev/null +++ b/cairo_programs/cairo/hints/alloc_felt_dict.cairo @@ -0,0 +1,3 @@ +fn main() { + let mut _balances: Felt252Dict = Default::default(); +} diff --git a/cairo_programs/cairo/hints/dict_get.cairo b/cairo_programs/cairo/hints/dict_get.cairo new file mode 100644 index 00000000..5e8ab0b7 --- /dev/null +++ b/cairo_programs/cairo/hints/dict_get.cairo @@ -0,0 +1,5 @@ +fn main() { + let mut balances: Felt252Dict = Default::default(); + balances.insert('Simon', 100); + balances.get('Simon'); +} diff --git a/cairo_programs/cairo/hints/dict_insert.cairo b/cairo_programs/cairo/hints/dict_insert.cairo new file mode 100644 index 00000000..abc66d10 --- /dev/null +++ b/cairo_programs/cairo/hints/dict_insert.cairo @@ -0,0 +1,4 @@ +fn main() { + let mut balances: Felt252Dict = Default::default(); + balances.insert('Simon', 100); +} From bd8b3f7ce01b09c68257a9b06607ac103d1ca31e Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 16 Jul 2024 20:50:07 +0200 Subject: [PATCH 07/78] feat: add extractBuffer and unit tests hint utils --- src/errors/virtualMachine.ts | 8 ++ src/vm/virtualMachine.test.ts | 211 ++++++++++++++++++++++++++++++++++ src/vm/virtualMachine.ts | 33 +++++- 3 files changed, 247 insertions(+), 5 deletions(-) diff --git a/src/errors/virtualMachine.ts b/src/errors/virtualMachine.ts index 09c41528..cc02c7f6 100644 --- a/src/errors/virtualMachine.ts +++ b/src/errors/virtualMachine.ts @@ -1,3 +1,4 @@ +import { ResOp } from 'hints/hintParamsSchema'; import { Felt } from 'primitives/felt'; import { Relocatable } from 'primitives/relocatable'; import { SegmentValue } from 'primitives/segmentValue'; @@ -59,6 +60,13 @@ export class UndefinedOp1 extends VirtualMachineError { } } +/** `resOp` is not of a valid type to extract buffer from it. */ +export class InvalidBufferResOp extends VirtualMachineError { + constructor(resOp: ResOp) { + super(`Cannot extract buffer from the given ResOp: ${resOp}`); + } +} + /** Cannot find Dictionnary at `address */ export class DictNotFound extends VirtualMachineError { constructor(address: Relocatable) { diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index 9a01c8cd..d05842f5 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -4,6 +4,7 @@ import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; import { DictNotFound, DictValueNotFound, + InvalidBufferResOp, UnusedRes, } from 'errors/virtualMachine'; @@ -20,6 +21,7 @@ import { Op1Src, } from './instruction'; import { Dictionnary, VirtualMachine } from './virtualMachine'; +import { CellRef, Operation, OpType, ResOp } from 'hints/hintParamsSchema'; const instructions = { InvalidAssertEq: new Instruction( @@ -524,6 +526,215 @@ describe('VirtualMachine', () => { }); }); + describe('hint processor', () => { + describe('cellRefToRelocatable', () => { + test.each([ + [{ register: Register.Ap, offset: 0 }, new Relocatable(1, 0)], + [{ register: Register.Ap, offset: 5 }, new Relocatable(1, 5)], + [{ register: Register.Fp, offset: 3 }, new Relocatable(1, 3)], + ])( + 'should properly read the cellRef', + (cellRef: CellRef, expected: Relocatable) => { + const vm = new VirtualMachine(); + expect(vm.cellRefToRelocatable(cellRef)).toEqual(expected); + } + ); + + test('should properly read a Felt', () => { + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const value = new Felt(10n); + const cellRef: CellRef = { register: Register.Ap, offset: 0 }; + vm.memory.assertEq(vm.ap, value); + expect(vm.getFelt(cellRef)).toEqual(value); + }); + + test('should properly read a Relocatable', () => { + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const value = new Relocatable(0, 10); + const cellRef: CellRef = { register: Register.Ap, offset: 0 }; + vm.memory.assertEq(vm.ap, value); + expect(vm.getRelocatable(cellRef)).toEqual(value); + }); + + test('should throw when reading a Relocatable as a Felt', () => { + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const value = new Relocatable(0, 10); + const cellRef: CellRef = { register: Register.Ap, offset: 0 }; + vm.memory.assertEq(vm.ap, value); + expect(() => vm.getFelt(cellRef)).toThrow(new ExpectedFelt(value)); + }); + + test('should throw when reading a Felt as a Relocatable', () => { + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const value = new Felt(10n); + const cellRef: CellRef = { register: Register.Ap, offset: 0 }; + vm.memory.assertEq(vm.ap, value); + expect(() => vm.getRelocatable(cellRef)).toThrow( + new ExpectedRelocatable(value) + ); + }); + + test('should properly read a pointer', () => { + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const cell: CellRef = { register: Register.Ap, offset: 0 }; + const offset = new Felt(3n); + const value = new Relocatable(0, 4); + vm.memory.assertEq(vm.ap, value); + expect(vm.getPointer(cell, offset)).toEqual(value.add(offset)); + }); + + test.each([ + [ + { + type: OpType.Deref, + cell: { register: Register.Ap, offset: 0 }, + }, + [{ register: Register.Ap, offset: 0 }, new Felt(0n)], + ], + [ + { + type: OpType.BinOp, + op: Operation.Add, + a: { register: Register.Fp, offset: 2 }, + b: { type: OpType.Immediate, value: new Felt(5n) }, + }, + [{ register: Register.Fp, offset: 2 }, new Felt(5n)], + ], + ])('should properly extract a buffer', (resOp: ResOp, expected) => { + const vm = new VirtualMachine(); + expect(vm.extractBuffer(resOp)).toEqual(expected as [CellRef, Felt]); + }); + + test.each([ + { + type: OpType.DoubleDeref, + cell: { register: Register.Ap, offset: 0 }, + offset: 1, + }, + + { + type: OpType.BinOp, + op: Operation.Add, + a: { register: Register.Fp, offset: 2 }, + b: { + type: OpType.Deref, + cell: { register: Register.Ap, offset: 5 }, + }, + }, + + { + type: OpType.BinOp, + op: Operation.Add, + a: { register: Register.Fp, offset: 2 }, + b: { + type: OpType.Deref, + cell: { register: Register.Ap, offset: 5 }, + }, + }, + { + type: OpType.Immediate, + value: new Felt(4n), + }, + ])( + 'should throw when extracting a buffer with the wrong resOp', + (resOp: ResOp) => { + const vm = new VirtualMachine(); + expect(() => vm.extractBuffer(resOp)).toThrow( + new InvalidBufferResOp(resOp) + ); + } + ); + + test.each([ + [ + { + type: OpType.Deref, + cell: { register: Register.Ap, offset: 0 }, + }, + new Felt(3n), + ], + [ + { + type: OpType.DoubleDeref, + cell: { register: Register.Ap, offset: 2 }, + offset: 1, + }, + new Felt(7n), + ], + [ + { + type: OpType.Immediate, + value: new Felt(5n), + }, + new Felt(5n), + ], + [ + { + type: OpType.BinOp, + op: Operation.Add, + a: { register: Register.Fp, offset: 0 }, + b: { type: OpType.Immediate, value: new Felt(5n) }, + }, + new Felt(8n), + ], + [ + { + type: OpType.BinOp, + op: Operation.Mul, + a: { register: Register.Fp, offset: 0 }, + b: { type: OpType.Immediate, value: new Felt(5n) }, + }, + new Felt(15n), + ], + [ + { + type: OpType.BinOp, + op: Operation.Add, + a: { register: Register.Ap, offset: 0 }, + b: { + type: OpType.Deref, + cell: { register: Register.Ap, offset: 1 }, + }, + }, + new Felt(10n), + ], + [ + { + type: OpType.BinOp, + op: Operation.Mul, + a: { register: Register.Ap, offset: 0 }, + b: { + type: OpType.Deref, + cell: { register: Register.Ap, offset: 1 }, + }, + }, + new Felt(21n), + ], + ])('should properly read ResOp', (resOp: ResOp, expected: Felt) => { + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const value0 = new Felt(3n); + const value1 = new Felt(7n); + const address = new Relocatable(1, 0); + vm.memory.assertEq(vm.ap, value0); + vm.memory.assertEq(vm.ap.add(1), value1); + vm.memory.assertEq(vm.ap.add(2), address); + expect(vm.getResOperandValue(resOp)).toEqual(expected); + }); + }); + }); + describe('Dictionnary', () => { test('should properly initialize the dict manager', () => { const vm = new VirtualMachine(); diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 5f18f34f..5d1e8e2d 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -10,6 +10,7 @@ import { UndefinedOp1, DictNotFound, DictValueNotFound, + InvalidBufferResOp, } from 'errors/virtualMachine'; import { InvalidCellRefRegister, UnknownHint } from 'errors/hints'; @@ -43,6 +44,7 @@ import { } from './instruction'; import { ScopeManager } from 'hints/scopeManager'; import { SquashedDictManager } from './squashedDict'; +import { allocFelt252Dict, AllocFelt252Dict } from 'hints/allocFelt252Dict'; export type TraceEntry = { pc: Relocatable; @@ -494,7 +496,7 @@ export class VirtualMachine { * * NOTE: used in Cairo hints */ - cellRefToRelocatable(cell: CellRef) { + cellRefToRelocatable(cell: CellRef): Relocatable { let register: Relocatable; switch (cell.register) { case Register.Ap: @@ -517,12 +519,10 @@ export class VirtualMachine { * NOTE: used in Cairo hints */ getPointer(cell: CellRef, offset: Felt) { - const address = this.memory.get( - this.cellRefToRelocatable(cell).add(offset) - ); + const address = this.memory.get(this.cellRefToRelocatable(cell)); if (!address || !isRelocatable(address)) throw new ExpectedRelocatable(address); - return address; + return address.add(offset); } /** @@ -608,6 +608,29 @@ export class VirtualMachine { } } + /** + * Return the address defined at `resOp`. + * + * This method assume that resOp points to a Relocatable. + * + * Only Deref and BinOp with Immediate value are valid for extracting a buffer. + * + * NOTE: Used in Cairo hints. + */ + extractBuffer(resOp: ResOp): [CellRef, Felt] { + switch (resOp.type) { + case OpType.Deref: + return [(resOp as Deref).cell, new Felt(0n)]; + case OpType.BinOp: + const binOp = resOp as BinOp; + if (binOp.b.type !== OpType.Immediate) + throw new InvalidBufferResOp(resOp); + return [binOp.a, (binOp.b as Immediate).value]; + default: + throw new InvalidBufferResOp(resOp); + } + } + newDict(): Relocatable { const dictAddr = this.memory.addSegment(); this.dictManager.set(dictAddr.segmentId, new Dictionnary()); From 258636a90b5691acc9e7d24defbc9898fb26bd07 Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 17 Jul 2024 10:03:17 +0200 Subject: [PATCH 08/78] feat: add AllocFelt252Dict hint --- src/hints/allocFelt252Dict.test.ts | 69 ++++++++++++++++++++++++++++++ src/hints/allocFelt252Dict.ts | 47 ++++++++++++++++++++ src/hints/hintName.ts | 1 + src/hints/hintSchema.ts | 7 ++- src/vm/virtualMachine.ts | 4 ++ 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/hints/allocFelt252Dict.test.ts create mode 100644 src/hints/allocFelt252Dict.ts diff --git a/src/hints/allocFelt252Dict.test.ts b/src/hints/allocFelt252Dict.test.ts new file mode 100644 index 00000000..1df2cf39 --- /dev/null +++ b/src/hints/allocFelt252Dict.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { allocFelt252DictParser } from './allocFelt252Dict'; +import { OpType } from './hintParamsSchema'; +import { Felt } from 'primitives/felt'; +import { segmentArenaHandler } from 'builtins/segmentArena'; +import { Relocatable } from 'primitives/relocatable'; + +const initSegmentArenaBuiltin = (vm: VirtualMachine) => { + const info = [ + vm.memory.addSegment(segmentArenaHandler), + new Felt(0n), + new Felt(0n), + ]; + const base = vm.memory.addSegment(segmentArenaHandler); + info.map((value, offset) => vm.memory.assertEq(base.add(offset), value)); + return base.add(info.length); +}; + +const ALLOC_FELT252_DICT = { + AllocFelt252Dict: { + segment_arena_ptr: { + Deref: { + register: 'AP', + offset: 0, + }, + }, + }, +}; + +describe('AllocFelt252Dict', () => { + test('should properly parse AllocFelt252Dict hint', () => { + const hint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); + expect(hint).toEqual({ + type: HintName.AllocFelt252Dict, + segment_arena_ptr: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 0, + }, + }, + }); + }); + + test('should properly execute AllocFelt252Dict hint', () => { + const hint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const arenaPtr = initSegmentArenaBuiltin(vm); + + expect(vm.memory.get(arenaPtr.sub(1))).toEqual(new Felt(0n)); + expect(vm.memory.get(arenaPtr.sub(2))).toEqual(new Felt(0n)); + expect(vm.memory.get(arenaPtr.sub(3))).toEqual(new Relocatable(2, 0)); + expect(vm.dictManager.size).toEqual(0); + + vm.memory.assertEq(vm.ap, arenaPtr); + vm.executeHint(hint); + + const newDictPtr = new Relocatable(4, 0); + expect(vm.memory.get(new Relocatable(2, 0))).toEqual(newDictPtr); + expect(vm.dictManager.size).toEqual(1); + expect(vm.dictManager.has(newDictPtr.segmentId)).toBeTrue(); + }); +}); diff --git a/src/hints/allocFelt252Dict.ts b/src/hints/allocFelt252Dict.ts new file mode 100644 index 00000000..a97f75e3 --- /dev/null +++ b/src/hints/allocFelt252Dict.ts @@ -0,0 +1,47 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { resOp, ResOp } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { isFelt, isRelocatable } from 'primitives/segmentValue'; +import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; +import { Felt } from 'primitives/felt'; + +/** Zod object to parse AllocFelt252Dict hint */ +export const allocFelt252DictParser = z + .object({ AllocFelt252Dict: z.object({ segment_arena_ptr: resOp }) }) + .transform(({ AllocFelt252Dict: { segment_arena_ptr } }) => ({ + type: HintName.AllocFelt252Dict, + segment_arena_ptr, + })); + +/** + * AllocFelt252Dict hint + * + * Add a new dictionnary. + */ +export type AllocFelt252Dict = z.infer; + +/** + * Add a new dictionnary. + * + * - Extract the current number of dictionnaries: `dictNumber` + * - Extract the segment info pointer: `infoPtr` + * - Create a new dictionnary at `infoPtr + 3 * dictNumber` + */ +export const allocFelt252Dict = ( + vm: VirtualMachine, + segmentArenaPtr: ResOp +) => { + const [cell, offset] = vm.extractBuffer(segmentArenaPtr); + const arenaPtr = vm.getPointer(cell, offset); + const dictNumber = vm.memory.get(arenaPtr.sub(2)); + if (!dictNumber || !isFelt(dictNumber)) throw new ExpectedFelt(dictNumber); + + const infoPtr = vm.memory.get(arenaPtr.sub(3)); + if (!infoPtr || !isRelocatable(infoPtr)) + throw new ExpectedRelocatable(infoPtr); + + const newDict = vm.newDict(); + vm.memory.assertEq(infoPtr.add(dictNumber.mul(new Felt(3n))), newDict); +}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index f490587f..a376679a 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -2,4 +2,5 @@ export enum HintName { AllocSegment = 'AllocSegment', TestLessThan = 'TestLessThan', + AllocFelt252Dict = 'AllocFelt252Dict', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 6a4899eb..64c3d556 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -2,9 +2,14 @@ import { z } from 'zod'; import { allocSegmentParser } from './allocSegment'; import { testLessThanParser } from './testLessThan'; +import { allocFelt252DictParser } from './allocFelt252Dict'; /** Zod object to parse any implemented hints */ -const hint = z.union([allocSegmentParser, testLessThanParser]); +const hint = z.union([ + allocSegmentParser, + testLessThanParser, + allocFelt252DictParser, +]); /** Zod object to parse an array of hints grouped on a given PC */ export const hintsGroup = z.tuple([z.number(), z.array(hint)]); diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 5d1e8e2d..db5e3513 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -89,6 +89,10 @@ export class VirtualMachine { const h = hint as TestLessThan; testLessThan(vm, h.lhs, h.rhs, h.dst); }, + [HintName.AllocFelt252Dict]: (vm, hint) => { + const h = hint as AllocFelt252Dict; + allocFelt252Dict(vm, h.segment_arena_ptr); + }, }; constructor() { From 76cbd3abda36e3c57e1b11f9df79b9651d9e5767 Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 17 Jul 2024 12:15:42 +0200 Subject: [PATCH 09/78] feat: add GetSegmentArenaIndex hint --- src/hints/getSegmentArenaIndex.test.ts | 94 ++++++++++++++++++++++++++ src/hints/getSegmentArenaIndex.ts | 46 +++++++++++++ src/hints/hintName.ts | 1 + src/hints/hintSchema.ts | 2 + src/vm/virtualMachine.test.ts | 2 +- src/vm/virtualMachine.ts | 20 +++++- 6 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/hints/getSegmentArenaIndex.test.ts create mode 100644 src/hints/getSegmentArenaIndex.ts diff --git a/src/hints/getSegmentArenaIndex.test.ts b/src/hints/getSegmentArenaIndex.test.ts new file mode 100644 index 00000000..c9245593 --- /dev/null +++ b/src/hints/getSegmentArenaIndex.test.ts @@ -0,0 +1,94 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { OpType } from './hintParamsSchema'; +import { Felt } from 'primitives/felt'; +import { segmentArenaHandler } from 'builtins/segmentArena'; +import { Relocatable } from 'primitives/relocatable'; +import { getSegmentArenaIndexParser } from './getSegmentArenaIndex'; +import { allocFelt252DictParser } from './allocFelt252Dict'; + +const initSegmentArenaBuiltin = (vm: VirtualMachine) => { + const info = [ + vm.memory.addSegment(segmentArenaHandler), + new Felt(0n), + new Felt(0n), + ]; + const base = vm.memory.addSegment(segmentArenaHandler); + info.map((value, offset) => vm.memory.assertEq(base.add(offset), value)); + return base.add(info.length); +}; + +const ALLOC_FELT252_DICT = { + AllocFelt252Dict: { + segment_arena_ptr: { + Deref: { + register: 'AP', + offset: 0, + }, + }, + }, +}; + +const GET_SEGMENT_ARENA_INDEX = { + GetSegmentArenaIndex: { + dict_end_ptr: { + Deref: { + register: 'AP', + offset: 1, + }, + }, + dict_index: { + register: 'AP', + offset: 2, + }, + }, +}; + +describe('GetSegmentArenaIndex', () => { + test('should properly parse GetSegmentArenaIndex hint', () => { + const hint = getSegmentArenaIndexParser.parse(GET_SEGMENT_ARENA_INDEX); + expect(hint).toEqual({ + type: HintName.GetSegmentArenaIndex, + dict_end_ptr: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 1, + }, + }, + dict_index: { + register: Register.Ap, + offset: 2, + }, + }); + }); + + test('should properly execute GetSegmentArenaIndex hint', () => { + const allocHint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); + const hint = getSegmentArenaIndexParser.parse(GET_SEGMENT_ARENA_INDEX); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const arenaPtr = initSegmentArenaBuiltin(vm); + + vm.memory.assertEq(vm.ap, arenaPtr); + vm.executeHint(allocHint); + + const newDictPtr = new Relocatable(4, 0); + const index = new Relocatable(4, 2); + vm.memory.assertEq(vm.ap.add(1), newDictPtr); + vm.memory.assertEq(vm.ap.add(2), index); + + vm.executeHint(hint); + + const dict = vm.dictManager.get(newDictPtr.segmentId); + expect(dict).not.toBeUndefined(); + + if (dict) { + expect(vm.memory.get(index)).toEqual(dict.id); + } + }); +}); diff --git a/src/hints/getSegmentArenaIndex.ts b/src/hints/getSegmentArenaIndex.ts new file mode 100644 index 00000000..2eed2c33 --- /dev/null +++ b/src/hints/getSegmentArenaIndex.ts @@ -0,0 +1,46 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { resOp, ResOp, cellRef, CellRef } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; + +/** Zod object to parse GetSegmentArenaIndex hint */ +export const getSegmentArenaIndexParser = z + .object({ + GetSegmentArenaIndex: z.object({ + dict_end_ptr: resOp, + dict_index: cellRef, + }), + }) + .transform(({ GetSegmentArenaIndex: { dict_end_ptr, dict_index } }) => ({ + type: HintName.GetSegmentArenaIndex, + dict_end_ptr, + dict_index, + })); + +/** + * GetSegmentArenaIndex hint + * + * Assert the index of the dictionnary to its segment. + * Used when finalizing the dictionnaries. + */ +export type GetSegmentArenaIndex = z.infer; + +/** + * Assert that `dictIndex` address stores the identifier of the + * dictionnary found at `dictEndPtr`. + * + * The identifier `id` the is dictionnary number, + * n for the n-th dictionnary, starting at 0. + * + */ +export const getSegmentArenaIndex = ( + vm: VirtualMachine, + dictEndPtr: ResOp, + dictIndex: CellRef +) => { + const index = vm.getRelocatable(dictIndex); + const address = vm.getPointer(...vm.extractBuffer(dictEndPtr)); + const dict = vm.getDict(address); + vm.memory.assertEq(index, dict.id); +}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index a376679a..53484f21 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -3,4 +3,5 @@ export enum HintName { AllocSegment = 'AllocSegment', TestLessThan = 'TestLessThan', AllocFelt252Dict = 'AllocFelt252Dict', + GetSegmentArenaIndex = 'GetSegmentArenaIndex', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 64c3d556..7466c4d8 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -3,12 +3,14 @@ import { z } from 'zod'; import { allocSegmentParser } from './allocSegment'; import { testLessThanParser } from './testLessThan'; import { allocFelt252DictParser } from './allocFelt252Dict'; +import { getSegmentArenaIndexParser } from './getSegmentArenaIndex'; /** Zod object to parse any implemented hints */ const hint = z.union([ allocSegmentParser, testLessThanParser, allocFelt252DictParser, + getSegmentArenaIndexParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index d05842f5..d6854336 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -745,7 +745,7 @@ describe('VirtualMachine', () => { const vm = new VirtualMachine(); const address = vm.newDict(); expect(address).toEqual(new Relocatable(0, 0)); - expect(vm.getDict(address)).toEqual(new Dictionnary()); + expect(vm.getDict(address)).toEqual(new Dictionnary(new Felt(BigInt(0)))); }); test('should properly set and get value of a dictionnary', () => { diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index db5e3513..e0cd50e0 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -45,6 +45,10 @@ import { import { ScopeManager } from 'hints/scopeManager'; import { SquashedDictManager } from './squashedDict'; import { allocFelt252Dict, AllocFelt252Dict } from 'hints/allocFelt252Dict'; +import { + getSegmentArenaIndex, + GetSegmentArenaIndex, +} from 'hints/getSegmentArenaIndex'; export type TraceEntry = { pc: Relocatable; @@ -63,7 +67,12 @@ export type RelocatedMemory = { value: Felt; }; -export class Dictionnary extends Map {} +export class Dictionnary extends Map { + constructor(public readonly id: Felt) { + super(); + this.id = id; + } +} export class VirtualMachine { private currentStep: bigint; @@ -93,6 +102,10 @@ export class VirtualMachine { const h = hint as AllocFelt252Dict; allocFelt252Dict(vm, h.segment_arena_ptr); }, + [HintName.GetSegmentArenaIndex]: (vm, hint) => { + const h = hint as GetSegmentArenaIndex; + getSegmentArenaIndex(vm, h.dict_end_ptr, h.dict_index); + }, }; constructor() { @@ -637,7 +650,10 @@ export class VirtualMachine { newDict(): Relocatable { const dictAddr = this.memory.addSegment(); - this.dictManager.set(dictAddr.segmentId, new Dictionnary()); + this.dictManager.set( + dictAddr.segmentId, + new Dictionnary(new Felt(BigInt(this.dictManager.size))) + ); return dictAddr; } From 957504d6a495b28a2879ae1085aac4c814bda41d Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 17 Jul 2024 12:17:48 +0200 Subject: [PATCH 10/78] refactor: inline pointer extraction --- src/hints/allocFelt252Dict.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hints/allocFelt252Dict.ts b/src/hints/allocFelt252Dict.ts index a97f75e3..378bfe5a 100644 --- a/src/hints/allocFelt252Dict.ts +++ b/src/hints/allocFelt252Dict.ts @@ -33,8 +33,7 @@ export const allocFelt252Dict = ( vm: VirtualMachine, segmentArenaPtr: ResOp ) => { - const [cell, offset] = vm.extractBuffer(segmentArenaPtr); - const arenaPtr = vm.getPointer(cell, offset); + const arenaPtr = vm.getPointer(...vm.extractBuffer(segmentArenaPtr)); const dictNumber = vm.memory.get(arenaPtr.sub(2)); if (!dictNumber || !isFelt(dictNumber)) throw new ExpectedFelt(dictNumber); From a5cc8b1394d68775d66445cae72d7c0e2a19fd9b Mon Sep 17 00:00:00 2001 From: malatrax Date: Thu, 18 Jul 2024 18:46:22 +0200 Subject: [PATCH 11/78] feat: add Felt252DictEntryInit --- src/hints/felt252DictEntryInit.test.ts | 103 +++++++++++++++++++++++++ src/hints/felt252DictEntryInit.ts | 50 ++++++++++++ src/hints/hintName.ts | 1 + src/hints/hintSchema.ts | 2 + src/vm/virtualMachine.ts | 8 ++ 5 files changed, 164 insertions(+) create mode 100644 src/hints/felt252DictEntryInit.test.ts create mode 100644 src/hints/felt252DictEntryInit.ts diff --git a/src/hints/felt252DictEntryInit.test.ts b/src/hints/felt252DictEntryInit.test.ts new file mode 100644 index 00000000..69df6b75 --- /dev/null +++ b/src/hints/felt252DictEntryInit.test.ts @@ -0,0 +1,103 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { OpType } from './hintParamsSchema'; +import { Felt } from 'primitives/felt'; +import { segmentArenaHandler } from 'builtins/segmentArena'; +import { Relocatable } from 'primitives/relocatable'; +import { allocFelt252DictParser } from './allocFelt252Dict'; +import { felt252DictEntryInitParser } from './felt252DictEntryInit'; + +const initSegmentArenaBuiltin = (vm: VirtualMachine) => { + const info = [ + vm.memory.addSegment(segmentArenaHandler), + new Felt(0n), + new Felt(0n), + ]; + const base = vm.memory.addSegment(segmentArenaHandler); + info.map((value, offset) => vm.memory.assertEq(base.add(offset), value)); + return base.add(info.length); +}; + +const ALLOC_FELT252_DICT = { + AllocFelt252Dict: { + segment_arena_ptr: { + Deref: { + register: 'AP', + offset: 0, + }, + }, + }, +}; + +const FELT252_DICT_ENTRY_INIT = { + Felt252DictEntryInit: { + dict_ptr: { + Deref: { + register: "AP", + offset: 1 + } + }, + key: { + Deref: { + register: "AP", + offset: 2 + } + } + } +} + +describe('Felt252DictEntryInit', () => { + test('should properly parse Felt252DictEntryInit hint', () => { + const hint = felt252DictEntryInitParser.parse(FELT252_DICT_ENTRY_INIT); + expect(hint).toEqual({ + type: HintName.Felt252DictEntryInit, + dictPtr: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 1, + }, + }, + key: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 2, + }, + }, + }); + }); + + test('should properly execute Felt252DictEntryInit hint', () => { + const allocHint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); + const hint = felt252DictEntryInitParser.parse(FELT252_DICT_ENTRY_INIT); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const arenaPtr = initSegmentArenaBuiltin(vm); + + vm.memory.assertEq(vm.ap, arenaPtr); + vm.executeHint(allocHint); + + const newDictPtr = new Relocatable(4, 0); + const key = new Felt(5n); + vm.memory.assertEq(vm.ap.add(1), newDictPtr); + vm.memory.assertEq(vm.ap.add(2), key); + + const dict = vm.dictManager.get(newDictPtr.segmentId); + expect(dict).not.toBeUndefined(); + + const keyValue = new Felt(13n) + dict?.set(key, keyValue) + + vm.executeHint(hint); + + if (dict) { + expect(dict.get(key)).toEqual(new Felt(13n)); + expect(vm.memory.get(newDictPtr.add(1))).toEqual(keyValue); + } + }); +}); diff --git a/src/hints/felt252DictEntryInit.ts b/src/hints/felt252DictEntryInit.ts new file mode 100644 index 00000000..b047450c --- /dev/null +++ b/src/hints/felt252DictEntryInit.ts @@ -0,0 +1,50 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { resOp, ResOp } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; + +/** Zod object to parse Felt252DictEntryInit hint */ +export const felt252DictEntryInitParser = z + .object({ + Felt252DictEntryInit: z.object({ + dict_ptr: resOp, + key: resOp, + }), + }) + .transform(({ Felt252DictEntryInit: { dict_ptr, key } }) => ({ + type: HintName.Felt252DictEntryInit, + dictPtr: dict_ptr, + key, + })); + +/** + * Felt252DictEntryInit hint + * + * Initialize the entry to be added to a given dictionnary. + */ +export type Felt252DictEntryInit = z.infer; + +/** + * Get the dictionnary `dict` at `dictPtr` + * Get the `key` to be initialized + * + * Read the current value at `key` if any, + * Initialize `dict[key]` to 0 if undefined. + * + * Assert the value at key to the `dict` segment. + * + */ +export const felt252DictEntryInit = ( + vm: VirtualMachine, + dictPtr: ResOp, + key: ResOp +) => { + const address = vm.getPointer(...vm.extractBuffer(dictPtr)); + const keyValue = vm.getResOperandValue(key); + const dict = vm.getDict(address); + const prevValue = dict.get(keyValue) || new Felt(0n); + dict.set(keyValue, prevValue); + vm.memory.assertEq(address.add(1), prevValue); +}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index 53484f21..3f641af2 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -4,4 +4,5 @@ export enum HintName { TestLessThan = 'TestLessThan', AllocFelt252Dict = 'AllocFelt252Dict', GetSegmentArenaIndex = 'GetSegmentArenaIndex', + Felt252DictEntryInit = 'Felt252DictEntryInit', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 7466c4d8..9e108710 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -4,6 +4,7 @@ import { allocSegmentParser } from './allocSegment'; import { testLessThanParser } from './testLessThan'; import { allocFelt252DictParser } from './allocFelt252Dict'; import { getSegmentArenaIndexParser } from './getSegmentArenaIndex'; +import { felt252DictEntryInitParser } from './felt252DictEntryInit'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -11,6 +12,7 @@ const hint = z.union([ testLessThanParser, allocFelt252DictParser, getSegmentArenaIndexParser, + felt252DictEntryInitParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index e0cd50e0..d7a64598 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -49,6 +49,10 @@ import { getSegmentArenaIndex, GetSegmentArenaIndex, } from 'hints/getSegmentArenaIndex'; +import { + felt252DictEntryInit, + Felt252DictEntryInit, +} from 'hints/felt252DictEntryInit'; export type TraceEntry = { pc: Relocatable; @@ -106,6 +110,10 @@ export class VirtualMachine { const h = hint as GetSegmentArenaIndex; getSegmentArenaIndex(vm, h.dict_end_ptr, h.dict_index); }, + [HintName.Felt252DictEntryInit]: (vm, hint) => { + const h = hint as Felt252DictEntryInit; + felt252DictEntryInit(vm, h.dictPtr, h.key); + }, }; constructor() { From b759763edb762a03a7c5baa223f7c5a8d70398fc Mon Sep 17 00:00:00 2001 From: malatrax Date: Thu, 18 Jul 2024 18:51:33 +0200 Subject: [PATCH 12/78] chore: enforce camelCase in hints --- src/hints/allocFelt252Dict.test.ts | 2 +- src/hints/allocFelt252Dict.ts | 2 +- src/vm/virtualMachine.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hints/allocFelt252Dict.test.ts b/src/hints/allocFelt252Dict.test.ts index 1df2cf39..06002c4d 100644 --- a/src/hints/allocFelt252Dict.test.ts +++ b/src/hints/allocFelt252Dict.test.ts @@ -36,7 +36,7 @@ describe('AllocFelt252Dict', () => { const hint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); expect(hint).toEqual({ type: HintName.AllocFelt252Dict, - segment_arena_ptr: { + segmentArenaPtr: { type: OpType.Deref, cell: { register: Register.Ap, diff --git a/src/hints/allocFelt252Dict.ts b/src/hints/allocFelt252Dict.ts index 378bfe5a..3eaa4ed9 100644 --- a/src/hints/allocFelt252Dict.ts +++ b/src/hints/allocFelt252Dict.ts @@ -12,7 +12,7 @@ export const allocFelt252DictParser = z .object({ AllocFelt252Dict: z.object({ segment_arena_ptr: resOp }) }) .transform(({ AllocFelt252Dict: { segment_arena_ptr } }) => ({ type: HintName.AllocFelt252Dict, - segment_arena_ptr, + segmentArenaPtr: segment_arena_ptr, })); /** diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index d7a64598..733c8f6a 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -104,7 +104,7 @@ export class VirtualMachine { }, [HintName.AllocFelt252Dict]: (vm, hint) => { const h = hint as AllocFelt252Dict; - allocFelt252Dict(vm, h.segment_arena_ptr); + allocFelt252Dict(vm, h.segmentArenaPtr); }, [HintName.GetSegmentArenaIndex]: (vm, hint) => { const h = hint as GetSegmentArenaIndex; From 70a0edf4b67ae5f78d5d727d0f07fbc8b4fc422c Mon Sep 17 00:00:00 2001 From: malatrax Date: Thu, 18 Jul 2024 21:59:47 +0200 Subject: [PATCH 13/78] feat: add Felt252DictUpdate hint --- src/hints/felt252DictUpdate.test.ts | 103 ++++++++++++++++++++++++++++ src/hints/felt252DictUpdate.ts | 52 ++++++++++++++ src/hints/hintName.ts | 1 + src/hints/hintSchema.ts | 2 + src/vm/virtualMachine.ts | 5 ++ 5 files changed, 163 insertions(+) create mode 100644 src/hints/felt252DictUpdate.test.ts create mode 100644 src/hints/felt252DictUpdate.ts diff --git a/src/hints/felt252DictUpdate.test.ts b/src/hints/felt252DictUpdate.test.ts new file mode 100644 index 00000000..70e1d758 --- /dev/null +++ b/src/hints/felt252DictUpdate.test.ts @@ -0,0 +1,103 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { OpType } from './hintParamsSchema'; +import { Felt } from 'primitives/felt'; +import { segmentArenaHandler } from 'builtins/segmentArena'; +import { Relocatable } from 'primitives/relocatable'; +import { allocFelt252DictParser } from './allocFelt252Dict'; +import { felt252DictUpdateParser } from './felt252DictUpdate'; + +const initSegmentArenaBuiltin = (vm: VirtualMachine) => { + const info = [ + vm.memory.addSegment(segmentArenaHandler), + new Felt(0n), + new Felt(0n), + ]; + const base = vm.memory.addSegment(segmentArenaHandler); + info.map((value, offset) => vm.memory.assertEq(base.add(offset), value)); + return base.add(info.length); +}; + +const ALLOC_FELT252_DICT = { + AllocFelt252Dict: { + segment_arena_ptr: { + Deref: { + register: 'AP', + offset: 0, + }, + }, + }, +}; + +const FELT252_DICT_UPDATE = { + Felt252DictUpdate: { + dict_ptr: { + Deref: { + register: 'AP', + offset: 1, + }, + }, + value: { + Deref: { + register: 'AP', + offset: 3, + }, + }, + }, +}; + +describe('Felt252DictUpdate', () => { + test('should properly parse Felt252DictEntryUpdate hint', () => { + const hint = felt252DictUpdateParser.parse(FELT252_DICT_UPDATE); + expect(hint).toEqual({ + type: HintName.Felt252DictUpdate, + dictPtr: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 1, + }, + }, + value: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 3, + }, + }, + }); + }); + + test('should properly execute Felt252DictUpdate hint', () => { + const allocHint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); + const hint = felt252DictUpdateParser.parse(FELT252_DICT_UPDATE); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const arenaPtr = initSegmentArenaBuiltin(vm); + + vm.memory.assertEq(vm.ap, arenaPtr); + vm.executeHint(allocHint); + + const newDictPtr = new Relocatable(4, 0); + const key = new Relocatable(1, 2); + const keyValue = new Felt(5n); + const value = new Felt(13n); + vm.memory.assertEq(vm.ap.add(1), newDictPtr.add(3)); + vm.memory.assertEq(newDictPtr, key); + vm.memory.assertEq(vm.ap.add(2), keyValue); + vm.memory.assertEq(vm.ap.add(3), value); + + const dict = vm.dictManager.get(newDictPtr.segmentId); + expect(dict).not.toBeUndefined(); + + vm.executeHint(hint); + + if (dict) { + expect(dict.get(keyValue)).toEqual(value); + } + }); +}); diff --git a/src/hints/felt252DictUpdate.ts b/src/hints/felt252DictUpdate.ts new file mode 100644 index 00000000..a9b1bd06 --- /dev/null +++ b/src/hints/felt252DictUpdate.ts @@ -0,0 +1,52 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { resOp, ResOp } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { isFelt, isRelocatable } from 'primitives/segmentValue'; +import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; + +/** Zod object to parse Felt252DictUpdate hint */ +export const felt252DictUpdateParser = z + .object({ + Felt252DictUpdate: z.object({ + dict_ptr: resOp, + value: resOp, + }), + }) + .transform(({ Felt252DictUpdate: { dict_ptr, value } }) => ({ + type: HintName.Felt252DictUpdate, + dictPtr: dict_ptr, + value, + })); + +/** + * Felt252DictUpdate hint + * + * Set a value in a dict: `dict[key] = value` + */ +export type Felt252DictUpdate = z.infer; + +/** + * Get the dictionnary `dict` at `dictPtr` + * + * Get the key at address `dict - 3` + * + * `dict[key] = value` + * + */ +export const felt252DictUpdate = ( + vm: VirtualMachine, + dictPtr: ResOp, + value: ResOp +) => { + const address = vm.getPointer(...vm.extractBuffer(dictPtr)); + const keyAddr = vm.memory.get(address.sub(3)); + if (!keyAddr || !isRelocatable(keyAddr)) + throw new ExpectedRelocatable(keyAddr); + const key = vm.memory.get(keyAddr); + if (!key || !isFelt(key)) throw new ExpectedFelt(key); + const val = vm.getResOperandValue(value); + const dict = vm.getDict(address); + dict.set(key, val); +}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index 3f641af2..416e7b12 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -5,4 +5,5 @@ export enum HintName { AllocFelt252Dict = 'AllocFelt252Dict', GetSegmentArenaIndex = 'GetSegmentArenaIndex', Felt252DictEntryInit = 'Felt252DictEntryInit', + Felt252DictUpdate = 'Felt252DictUpdate' } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 9e108710..97700af9 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -5,6 +5,7 @@ import { testLessThanParser } from './testLessThan'; import { allocFelt252DictParser } from './allocFelt252Dict'; import { getSegmentArenaIndexParser } from './getSegmentArenaIndex'; import { felt252DictEntryInitParser } from './felt252DictEntryInit'; +import { felt252DictUpdateParser } from './felt252DictUpdate'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -13,6 +14,7 @@ const hint = z.union([ allocFelt252DictParser, getSegmentArenaIndexParser, felt252DictEntryInitParser, + felt252DictUpdateParser ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 733c8f6a..5de78d67 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -53,6 +53,7 @@ import { felt252DictEntryInit, Felt252DictEntryInit, } from 'hints/felt252DictEntryInit'; +import { Felt252DictUpdate, felt252DictUpdate } from 'hints/felt252DictUpdate'; export type TraceEntry = { pc: Relocatable; @@ -114,6 +115,10 @@ export class VirtualMachine { const h = hint as Felt252DictEntryInit; felt252DictEntryInit(vm, h.dictPtr, h.key); }, + [HintName.Felt252DictUpdate]: (vm, hint) => { + const h = hint as Felt252DictUpdate; + felt252DictUpdate(vm, h.dictPtr, h.value); + }, }; constructor() { From 19adaeadad526c126ec15aa193dca8d01bb8d086 Mon Sep 17 00:00:00 2001 From: malatrax Date: Fri, 19 Jul 2024 09:36:08 +0200 Subject: [PATCH 14/78] refactor: simplify felt252DictUpdate --- src/hints/felt252DictUpdate.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hints/felt252DictUpdate.ts b/src/hints/felt252DictUpdate.ts index a9b1bd06..16c285c7 100644 --- a/src/hints/felt252DictUpdate.ts +++ b/src/hints/felt252DictUpdate.ts @@ -41,12 +41,10 @@ export const felt252DictUpdate = ( value: ResOp ) => { const address = vm.getPointer(...vm.extractBuffer(dictPtr)); - const keyAddr = vm.memory.get(address.sub(3)); - if (!keyAddr || !isRelocatable(keyAddr)) - throw new ExpectedRelocatable(keyAddr); - const key = vm.memory.get(keyAddr); + const keyAddress = vm.memory.get(address.sub(3)); + if (!keyAddress || !isRelocatable(keyAddress)) + throw new ExpectedRelocatable(keyAddress); + const key = vm.memory.get(keyAddress); if (!key || !isFelt(key)) throw new ExpectedFelt(key); - const val = vm.getResOperandValue(value); - const dict = vm.getDict(address); - dict.set(key, val); + vm.getDict(address).set(key, vm.getResOperandValue(value)); }; From 5d9ed1aa4784717fa8240b850060f5cb8d5f7f46 Mon Sep 17 00:00:00 2001 From: malatrax Date: Fri, 19 Jul 2024 10:00:26 +0200 Subject: [PATCH 15/78] fix: support Relocatable as dict value --- src/errors/memory.ts | 7 +++++ src/hints/felt252DictUpdate.test.ts | 49 +++++++++++++++-------------- src/hints/felt252DictUpdate.ts | 16 ++++++++-- 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/errors/memory.ts b/src/errors/memory.ts index 67ce35d0..752d152e 100644 --- a/src/errors/memory.ts +++ b/src/errors/memory.ts @@ -24,3 +24,10 @@ export class SegmentOutOfBounds extends MemoryError { ); } } + +/** Expected a SegmentValue but read `undefined` */ +export class UndefinedSegmentValue extends MemoryError { + constructor() { + super(`Expected a SegmentValue, read undefined`); + } +} diff --git a/src/hints/felt252DictUpdate.test.ts b/src/hints/felt252DictUpdate.test.ts index 70e1d758..7cee0874 100644 --- a/src/hints/felt252DictUpdate.test.ts +++ b/src/hints/felt252DictUpdate.test.ts @@ -9,6 +9,7 @@ import { segmentArenaHandler } from 'builtins/segmentArena'; import { Relocatable } from 'primitives/relocatable'; import { allocFelt252DictParser } from './allocFelt252Dict'; import { felt252DictUpdateParser } from './felt252DictUpdate'; +import { SegmentValue } from 'primitives/segmentValue'; const initSegmentArenaBuiltin = (vm: VirtualMachine) => { const info = [ @@ -71,33 +72,35 @@ describe('Felt252DictUpdate', () => { }); }); - test('should properly execute Felt252DictUpdate hint', () => { - const allocHint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); - const hint = felt252DictUpdateParser.parse(FELT252_DICT_UPDATE); - const vm = new VirtualMachine(); - vm.memory.addSegment(); - vm.memory.addSegment(); - const arenaPtr = initSegmentArenaBuiltin(vm); + test.each([new Felt(13n), new Relocatable(2, 0)])( + 'should properly execute Felt252DictUpdate hint', + (value: SegmentValue) => { + const allocHint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); + const hint = felt252DictUpdateParser.parse(FELT252_DICT_UPDATE); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const arenaPtr = initSegmentArenaBuiltin(vm); - vm.memory.assertEq(vm.ap, arenaPtr); - vm.executeHint(allocHint); + vm.memory.assertEq(vm.ap, arenaPtr); + vm.executeHint(allocHint); - const newDictPtr = new Relocatable(4, 0); - const key = new Relocatable(1, 2); - const keyValue = new Felt(5n); - const value = new Felt(13n); - vm.memory.assertEq(vm.ap.add(1), newDictPtr.add(3)); - vm.memory.assertEq(newDictPtr, key); - vm.memory.assertEq(vm.ap.add(2), keyValue); - vm.memory.assertEq(vm.ap.add(3), value); + const newDictPtr = new Relocatable(4, 0); + const key = new Relocatable(1, 2); + const keyValue = new Felt(5n); + vm.memory.assertEq(vm.ap.add(1), newDictPtr.add(3)); + vm.memory.assertEq(newDictPtr, key); + vm.memory.assertEq(vm.ap.add(2), keyValue); + vm.memory.assertEq(vm.ap.add(3), value); - const dict = vm.dictManager.get(newDictPtr.segmentId); - expect(dict).not.toBeUndefined(); + const dict = vm.dictManager.get(newDictPtr.segmentId); + expect(dict).not.toBeUndefined(); - vm.executeHint(hint); + vm.executeHint(hint); - if (dict) { - expect(dict.get(keyValue)).toEqual(value); + if (dict) { + expect(dict.get(keyValue)).toEqual(value); + } } - }); + ); }); diff --git a/src/hints/felt252DictUpdate.ts b/src/hints/felt252DictUpdate.ts index 16c285c7..0d584668 100644 --- a/src/hints/felt252DictUpdate.ts +++ b/src/hints/felt252DictUpdate.ts @@ -1,10 +1,12 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; -import { resOp, ResOp } from 'hints/hintParamsSchema'; +import { Deref, OpType, resOp, ResOp } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; import { isFelt, isRelocatable } from 'primitives/segmentValue'; import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; +import { Relocatable } from 'primitives/relocatable'; +import { UndefinedSegmentValue } from 'errors/memory'; /** Zod object to parse Felt252DictUpdate hint */ export const felt252DictUpdateParser = z @@ -46,5 +48,15 @@ export const felt252DictUpdate = ( throw new ExpectedRelocatable(keyAddress); const key = vm.memory.get(keyAddress); if (!key || !isFelt(key)) throw new ExpectedFelt(key); - vm.getDict(address).set(key, vm.getResOperandValue(value)); + const val = + value.type === OpType.Deref + ? vm.memory.get( + new Relocatable( + (value as Deref).cell.register, + (value as Deref).cell.offset + ) + ) + : vm.getResOperandValue(value); + if (!val) throw new UndefinedSegmentValue(); + vm.getDict(address).set(key, val); }; From c4d3b2ac424205909d221dcaa34d924b97427792 Mon Sep 17 00:00:00 2001 From: malatrax Date: Fri, 19 Jul 2024 14:38:14 +0200 Subject: [PATCH 16/78] fix: use proper Felt252DictEntryUpdate hint name --- ...test.ts => felt252DictEntryUpdate.test.ts} | 20 +++--- src/hints/felt252DictEntryUpdate.ts | 53 ++++++++++++++++ src/hints/felt252DictUpdate.ts | 62 ------------------- src/hints/hintName.ts | 4 +- src/hints/hintSchema.ts | 5 +- src/vm/virtualMachine.ts | 18 ++++-- 6 files changed, 82 insertions(+), 80 deletions(-) rename src/hints/{felt252DictUpdate.test.ts => felt252DictEntryUpdate.test.ts} (83%) create mode 100644 src/hints/felt252DictEntryUpdate.ts delete mode 100644 src/hints/felt252DictUpdate.ts diff --git a/src/hints/felt252DictUpdate.test.ts b/src/hints/felt252DictEntryUpdate.test.ts similarity index 83% rename from src/hints/felt252DictUpdate.test.ts rename to src/hints/felt252DictEntryUpdate.test.ts index 7cee0874..2b8d777b 100644 --- a/src/hints/felt252DictUpdate.test.ts +++ b/src/hints/felt252DictEntryUpdate.test.ts @@ -8,7 +8,7 @@ import { Felt } from 'primitives/felt'; import { segmentArenaHandler } from 'builtins/segmentArena'; import { Relocatable } from 'primitives/relocatable'; import { allocFelt252DictParser } from './allocFelt252Dict'; -import { felt252DictUpdateParser } from './felt252DictUpdate'; +import { felt252DictEntryUpdateParser } from './felt252DictEntryUpdate'; import { SegmentValue } from 'primitives/segmentValue'; const initSegmentArenaBuiltin = (vm: VirtualMachine) => { @@ -33,8 +33,8 @@ const ALLOC_FELT252_DICT = { }, }; -const FELT252_DICT_UPDATE = { - Felt252DictUpdate: { +const FELT252_DICT_ENTRY_UPDATE = { + Felt252DictEntryUpdate: { dict_ptr: { Deref: { register: 'AP', @@ -50,11 +50,11 @@ const FELT252_DICT_UPDATE = { }, }; -describe('Felt252DictUpdate', () => { +describe('Felt252DictEntryUpdate', () => { test('should properly parse Felt252DictEntryUpdate hint', () => { - const hint = felt252DictUpdateParser.parse(FELT252_DICT_UPDATE); + const hint = felt252DictEntryUpdateParser.parse(FELT252_DICT_ENTRY_UPDATE); expect(hint).toEqual({ - type: HintName.Felt252DictUpdate, + type: HintName.Felt252DictEntryUpdate, dictPtr: { type: OpType.Deref, cell: { @@ -76,7 +76,9 @@ describe('Felt252DictUpdate', () => { 'should properly execute Felt252DictUpdate hint', (value: SegmentValue) => { const allocHint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); - const hint = felt252DictUpdateParser.parse(FELT252_DICT_UPDATE); + const hint = felt252DictEntryUpdateParser.parse( + FELT252_DICT_ENTRY_UPDATE + ); const vm = new VirtualMachine(); vm.memory.addSegment(); vm.memory.addSegment(); @@ -86,11 +88,9 @@ describe('Felt252DictUpdate', () => { vm.executeHint(allocHint); const newDictPtr = new Relocatable(4, 0); - const key = new Relocatable(1, 2); const keyValue = new Felt(5n); vm.memory.assertEq(vm.ap.add(1), newDictPtr.add(3)); - vm.memory.assertEq(newDictPtr, key); - vm.memory.assertEq(vm.ap.add(2), keyValue); + vm.memory.assertEq(newDictPtr, keyValue); vm.memory.assertEq(vm.ap.add(3), value); const dict = vm.dictManager.get(newDictPtr.segmentId); diff --git a/src/hints/felt252DictEntryUpdate.ts b/src/hints/felt252DictEntryUpdate.ts new file mode 100644 index 00000000..63456a29 --- /dev/null +++ b/src/hints/felt252DictEntryUpdate.ts @@ -0,0 +1,53 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { Deref, OpType, resOp, ResOp } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { isFelt } from 'primitives/segmentValue'; +import { ExpectedFelt } from 'errors/primitives'; + +/** Zod object to parse Felt252DictEntryUpdate hint */ +export const felt252DictEntryUpdateParser = z + .object({ + Felt252DictEntryUpdate: z.object({ + dict_ptr: resOp, + value: resOp, + }), + }) + .transform(({ Felt252DictEntryUpdate: { dict_ptr, value } }) => ({ + type: HintName.Felt252DictEntryUpdate, + dictPtr: dict_ptr, + value, + })); + +/** + * Felt252DictEntryUpdate hint + * + * Set a value in a dict: `dict[key] = value` + */ +export type Felt252DictEntryUpdate = z.infer< + typeof felt252DictEntryUpdateParser +>; + +/** + * Get the dictionnary `dict` at `dictPtr` + * + * Get the key at address `dict - 3` + * + * `dict[key] = value` + * + */ +export const felt252DictEntryUpdate = ( + vm: VirtualMachine, + dictPtr: ResOp, + value: ResOp +) => { + const address = vm.getPointer(...vm.extractBuffer(dictPtr)); + const key = vm.memory.get(address.sub(3)); + if (!key || !isFelt(key)) throw new ExpectedFelt(key); + const val = + value.type === OpType.Deref + ? vm.getSegmentValue((value as Deref).cell) + : vm.getResOperandValue(value); + vm.getDict(address).set(key, val); +}; diff --git a/src/hints/felt252DictUpdate.ts b/src/hints/felt252DictUpdate.ts deleted file mode 100644 index 0d584668..00000000 --- a/src/hints/felt252DictUpdate.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { z } from 'zod'; - -import { VirtualMachine } from 'vm/virtualMachine'; -import { Deref, OpType, resOp, ResOp } from 'hints/hintParamsSchema'; -import { HintName } from 'hints/hintName'; -import { isFelt, isRelocatable } from 'primitives/segmentValue'; -import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; -import { Relocatable } from 'primitives/relocatable'; -import { UndefinedSegmentValue } from 'errors/memory'; - -/** Zod object to parse Felt252DictUpdate hint */ -export const felt252DictUpdateParser = z - .object({ - Felt252DictUpdate: z.object({ - dict_ptr: resOp, - value: resOp, - }), - }) - .transform(({ Felt252DictUpdate: { dict_ptr, value } }) => ({ - type: HintName.Felt252DictUpdate, - dictPtr: dict_ptr, - value, - })); - -/** - * Felt252DictUpdate hint - * - * Set a value in a dict: `dict[key] = value` - */ -export type Felt252DictUpdate = z.infer; - -/** - * Get the dictionnary `dict` at `dictPtr` - * - * Get the key at address `dict - 3` - * - * `dict[key] = value` - * - */ -export const felt252DictUpdate = ( - vm: VirtualMachine, - dictPtr: ResOp, - value: ResOp -) => { - const address = vm.getPointer(...vm.extractBuffer(dictPtr)); - const keyAddress = vm.memory.get(address.sub(3)); - if (!keyAddress || !isRelocatable(keyAddress)) - throw new ExpectedRelocatable(keyAddress); - const key = vm.memory.get(keyAddress); - if (!key || !isFelt(key)) throw new ExpectedFelt(key); - const val = - value.type === OpType.Deref - ? vm.memory.get( - new Relocatable( - (value as Deref).cell.register, - (value as Deref).cell.offset - ) - ) - : vm.getResOperandValue(value); - if (!val) throw new UndefinedSegmentValue(); - vm.getDict(address).set(key, val); -}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index 416e7b12..6d09a36a 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -3,7 +3,7 @@ export enum HintName { AllocSegment = 'AllocSegment', TestLessThan = 'TestLessThan', AllocFelt252Dict = 'AllocFelt252Dict', - GetSegmentArenaIndex = 'GetSegmentArenaIndex', Felt252DictEntryInit = 'Felt252DictEntryInit', - Felt252DictUpdate = 'Felt252DictUpdate' + Felt252DictEntryUpdate = 'Felt252DictEntryUpdate', + GetSegmentArenaIndex = 'GetSegmentArenaIndex', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 97700af9..28bbf34c 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -5,7 +5,8 @@ import { testLessThanParser } from './testLessThan'; import { allocFelt252DictParser } from './allocFelt252Dict'; import { getSegmentArenaIndexParser } from './getSegmentArenaIndex'; import { felt252DictEntryInitParser } from './felt252DictEntryInit'; -import { felt252DictUpdateParser } from './felt252DictUpdate'; +import { felt252DictEntryUpdateParser } from './felt252DictEntryUpdate'; +import { initSquashDataParser } from './initSquashData'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -14,7 +15,7 @@ const hint = z.union([ allocFelt252DictParser, getSegmentArenaIndexParser, felt252DictEntryInitParser, - felt252DictUpdateParser + felt252DictEntryUpdateParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 5de78d67..e5e72649 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -53,7 +53,11 @@ import { felt252DictEntryInit, Felt252DictEntryInit, } from 'hints/felt252DictEntryInit'; -import { Felt252DictUpdate, felt252DictUpdate } from 'hints/felt252DictUpdate'; +import { + Felt252DictEntryUpdate, + felt252DictEntryUpdate, +} from 'hints/felt252DictEntryUpdate'; +import { UndefinedSegmentValue } from 'errors/memory'; export type TraceEntry = { pc: Relocatable; @@ -115,9 +119,9 @@ export class VirtualMachine { const h = hint as Felt252DictEntryInit; felt252DictEntryInit(vm, h.dictPtr, h.key); }, - [HintName.Felt252DictUpdate]: (vm, hint) => { - const h = hint as Felt252DictUpdate; - felt252DictUpdate(vm, h.dictPtr, h.value); + [HintName.Felt252DictEntryUpdate]: (vm, hint) => { + const h = hint as Felt252DictEntryUpdate; + felt252DictEntryUpdate(vm, h.dictPtr, h.value); }, }; @@ -581,6 +585,12 @@ export class VirtualMachine { return value; } + getSegmentValue(cell: CellRef): SegmentValue { + const value = this.memory.get(this.cellRefToRelocatable(cell)); + if (!value) throw new UndefinedSegmentValue(); + return value; + } + /** * Return the value defined by `resOp` * From 09ebd9852ef8bde32593890c38d8f773bdd03bfb Mon Sep 17 00:00:00 2001 From: malatrax Date: Fri, 19 Jul 2024 14:38:33 +0200 Subject: [PATCH 17/78] fix: fix getSegmentArenaIndex hint --- src/hints/getSegmentArenaIndex.test.ts | 4 +--- src/hints/getSegmentArenaIndex.ts | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/hints/getSegmentArenaIndex.test.ts b/src/hints/getSegmentArenaIndex.test.ts index c9245593..05e407f7 100644 --- a/src/hints/getSegmentArenaIndex.test.ts +++ b/src/hints/getSegmentArenaIndex.test.ts @@ -78,9 +78,7 @@ describe('GetSegmentArenaIndex', () => { vm.executeHint(allocHint); const newDictPtr = new Relocatable(4, 0); - const index = new Relocatable(4, 2); vm.memory.assertEq(vm.ap.add(1), newDictPtr); - vm.memory.assertEq(vm.ap.add(2), index); vm.executeHint(hint); @@ -88,7 +86,7 @@ describe('GetSegmentArenaIndex', () => { expect(dict).not.toBeUndefined(); if (dict) { - expect(vm.memory.get(index)).toEqual(dict.id); + expect(vm.memory.get(vm.ap.add(2))).toEqual(dict.id); } }); }); diff --git a/src/hints/getSegmentArenaIndex.ts b/src/hints/getSegmentArenaIndex.ts index 2eed2c33..26d38e70 100644 --- a/src/hints/getSegmentArenaIndex.ts +++ b/src/hints/getSegmentArenaIndex.ts @@ -39,8 +39,7 @@ export const getSegmentArenaIndex = ( dictEndPtr: ResOp, dictIndex: CellRef ) => { - const index = vm.getRelocatable(dictIndex); const address = vm.getPointer(...vm.extractBuffer(dictEndPtr)); const dict = vm.getDict(address); - vm.memory.assertEq(index, dict.id); + vm.memory.assertEq(vm.cellRefToRelocatable(dictIndex), dict.id); }; From feb3f82909358fbf3232c8883bcd5c47f8875502 Mon Sep 17 00:00:00 2001 From: malatrax Date: Fri, 19 Jul 2024 14:38:59 +0200 Subject: [PATCH 18/78] feat: add InitSquashData hint --- src/hints/hintName.ts | 1 + src/hints/hintSchema.ts | 1 + src/hints/initSquashData.ts | 92 +++++++++++++++++++++++++++++++++++++ src/vm/virtualMachine.ts | 12 +++++ 4 files changed, 106 insertions(+) create mode 100644 src/hints/initSquashData.ts diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index 6d09a36a..7ebc13ae 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -6,4 +6,5 @@ export enum HintName { Felt252DictEntryInit = 'Felt252DictEntryInit', Felt252DictEntryUpdate = 'Felt252DictEntryUpdate', GetSegmentArenaIndex = 'GetSegmentArenaIndex', + InitSquashData = 'InitSquashData', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 28bbf34c..75902d43 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -16,6 +16,7 @@ const hint = z.union([ getSegmentArenaIndexParser, felt252DictEntryInitParser, felt252DictEntryUpdateParser, + initSquashDataParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/hints/initSquashData.ts b/src/hints/initSquashData.ts new file mode 100644 index 00000000..247eeca1 --- /dev/null +++ b/src/hints/initSquashData.ts @@ -0,0 +1,92 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { CellRef, cellRef, resOp, ResOp } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { isFelt } from 'primitives/segmentValue'; +import { ExpectedFelt } from 'errors/primitives'; +import { Felt } from 'primitives/felt'; + +/** Zod object to parse InitSquashData hint */ +export const initSquashDataParser = z + .object({ + InitSquashData: z.object({ + dict_accesses: resOp, + ptr_diff: resOp, + n_accesses: resOp, + big_keys: cellRef, + first_key: cellRef, + }), + }) + .transform( + ({ + InitSquashData: { + dict_accesses, + ptr_diff, + n_accesses, + big_keys, + first_key, + }, + }) => ({ + type: HintName.InitSquashData, + dictAccesses: dict_accesses, + ptrDiff: ptr_diff, + nAccesses: n_accesses, + bigKeys: big_keys, + firstKey: first_key, + }) + ); + +/** + * InitSquashData hint + * + * Set a value in a dict: `dict[key] = value` + */ +export type InitSquashData = z.infer; + +/** + * Get the dictionnary `dict` at `dictPtr` + * + * Get the key at address `dict - 3` + * + * `dict[key] = value` + * + */ +export const initSquashData = ( + vm: VirtualMachine, + dictAccesses: ResOp, + ptrDiff: ResOp, + nAccesses: ResOp, + bigKeys: CellRef, + firstKey: CellRef +) => { + const dictAccessSize = 3; + const address = vm.getPointer(...vm.extractBuffer(dictAccesses)); + if (Number(vm.getResOperandValue(ptrDiff)) % dictAccessSize) + throw new Error( + 'Accessess array size must be divisible by the dict size (3)' + ); + const nbAccesses = Number(vm.getResOperandValue(nAccesses)); + for (let i = 0; i < nbAccesses; i++) { + const key = vm.memory.get(address.add(i * dictAccessSize)); + if (!key || !isFelt(key)) throw new ExpectedFelt(key); + vm.squashedDictManager.insert(key, new Felt(BigInt(i))); + } + vm.squashedDictManager.keyToIndices.forEach((value, key) => { + value.reverse(); + vm.squashedDictManager.keys.push(key); + }); + vm.squashedDictManager.keys.sort((a, b) => (a < b ? 1 : a > b ? -1 : 0)); + const bigKeysAddress = vm.getRelocatable(bigKeys); + vm.memory.assertEq( + bigKeysAddress, + vm.squashedDictManager.keys[0] > new Felt(1n << 128n) + ? new Felt(1n) + : new Felt(0n) + ); + const firstKeyAddress = vm.getRelocatable(firstKey); + vm.memory.assertEq( + firstKeyAddress, + vm.squashedDictManager.keys[vm.squashedDictManager.keys.length - 1] + ); +}; diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index e5e72649..d5555fe5 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -57,6 +57,7 @@ import { Felt252DictEntryUpdate, felt252DictEntryUpdate, } from 'hints/felt252DictEntryUpdate'; +import { initSquashData, InitSquashData } from 'hints/initSquashData'; import { UndefinedSegmentValue } from 'errors/memory'; export type TraceEntry = { @@ -123,6 +124,17 @@ export class VirtualMachine { const h = hint as Felt252DictEntryUpdate; felt252DictEntryUpdate(vm, h.dictPtr, h.value); }, + [HintName.InitSquashData]: (vm, hint) => { + const h = hint as InitSquashData; + initSquashData( + vm, + h.dictAccesses, + h.ptrDiff, + h.nAccesses, + h.bigKeys, + h.firstKey + ); + }, }; constructor() { From 2faacfea1c23033759f8b03b2e921c6bc3e32cb5 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 08:02:57 +0200 Subject: [PATCH 19/78] fix: handle relocatable addition to a felt --- src/primitives/felt.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/primitives/felt.ts b/src/primitives/felt.ts index 024fd92b..f33e0716 100644 --- a/src/primitives/felt.ts +++ b/src/primitives/felt.ts @@ -3,6 +3,7 @@ import { CURVE } from '@scure/starknet'; import { CannotDivideByZero, ExpectedFelt } from 'errors/primitives'; import { SegmentValue, isFelt, isRelocatable } from './segmentValue'; +import { Relocatable } from './relocatable'; export class Felt { private inner: bigint; @@ -29,7 +30,13 @@ export class Felt { this.inner = _inner % Felt.PRIME; } - add(other: SegmentValue): Felt { + add(other: Felt): Felt; + add(other: Relocatable): Relocatable; + add(other: SegmentValue): SegmentValue; + add(other: SegmentValue): SegmentValue { + if (isRelocatable(other)) { + return other.add(this); + } if (!isFelt(other)) { throw new ExpectedFelt(other); } @@ -37,7 +44,10 @@ export class Felt { return new Felt(this.inner + other.inner); } - sub(other: SegmentValue): Felt { + sub(other: Felt): Felt; + sub(other: Relocatable): never; + sub(other: SegmentValue): SegmentValue; + sub(other: SegmentValue): SegmentValue { if (!isFelt(other)) { throw new ExpectedFelt(other); } From 9ffd8d1c0d5e1dd8e6e5464e58de8b109b24ec62 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 08:19:24 +0200 Subject: [PATCH 20/78] fix: add JSDoc --- src/hints/initSquashData.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/hints/initSquashData.ts b/src/hints/initSquashData.ts index 247eeca1..3dc8deca 100644 --- a/src/hints/initSquashData.ts +++ b/src/hints/initSquashData.ts @@ -40,16 +40,16 @@ export const initSquashDataParser = z /** * InitSquashData hint * - * Set a value in a dict: `dict[key] = value` + * Initialize the squashed dictionnary data. */ export type InitSquashData = z.infer; /** - * Get the dictionnary `dict` at `dictPtr` + * Initialize the squashed dictionnary * - * Get the key at address `dict - 3` - * - * `dict[key] = value` + * - Insert all accessed keys + * - Assert the biggest key to `bigKeys` address + * - Assert the smallest (first) key to `firstKey` address * */ export const initSquashData = ( @@ -72,21 +72,19 @@ export const initSquashData = ( if (!key || !isFelt(key)) throw new ExpectedFelt(key); vm.squashedDictManager.insert(key, new Felt(BigInt(i))); } - vm.squashedDictManager.keyToIndices.forEach((value, key) => { - value.reverse(); + vm.squashedDictManager.keyToIndices.forEach((values, key) => { + values.reverse(); vm.squashedDictManager.keys.push(key); }); vm.squashedDictManager.keys.sort((a, b) => (a < b ? 1 : a > b ? -1 : 0)); - const bigKeysAddress = vm.getRelocatable(bigKeys); vm.memory.assertEq( - bigKeysAddress, + vm.cellRefToRelocatable(bigKeys), vm.squashedDictManager.keys[0] > new Felt(1n << 128n) ? new Felt(1n) : new Felt(0n) ); - const firstKeyAddress = vm.getRelocatable(firstKey); vm.memory.assertEq( - firstKeyAddress, + vm.cellRefToRelocatable(firstKey), vm.squashedDictManager.keys[vm.squashedDictManager.keys.length - 1] ); }; From 0ea457df3606994f3d995b751b113386d5ace538 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 08:30:46 +0200 Subject: [PATCH 21/78] feat: add GetCurrentAccessIndex hint --- src/hints/felt252DictEntryInit.test.ts | 22 ++++++++--------- src/hints/getCurrentAccessIndex.ts | 34 ++++++++++++++++++++++++++ src/hints/hintName.ts | 1 + src/hints/hintSchema.ts | 2 ++ src/vm/virtualMachine.ts | 11 ++++++++- 5 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 src/hints/getCurrentAccessIndex.ts diff --git a/src/hints/felt252DictEntryInit.test.ts b/src/hints/felt252DictEntryInit.test.ts index 69df6b75..79adb981 100644 --- a/src/hints/felt252DictEntryInit.test.ts +++ b/src/hints/felt252DictEntryInit.test.ts @@ -36,18 +36,18 @@ const FELT252_DICT_ENTRY_INIT = { Felt252DictEntryInit: { dict_ptr: { Deref: { - register: "AP", - offset: 1 - } + register: 'AP', + offset: 1, + }, }, key: { Deref: { - register: "AP", - offset: 2 - } - } - } -} + register: 'AP', + offset: 2, + }, + }, + }, +}; describe('Felt252DictEntryInit', () => { test('should properly parse Felt252DictEntryInit hint', () => { @@ -90,8 +90,8 @@ describe('Felt252DictEntryInit', () => { const dict = vm.dictManager.get(newDictPtr.segmentId); expect(dict).not.toBeUndefined(); - const keyValue = new Felt(13n) - dict?.set(key, keyValue) + const keyValue = new Felt(13n); + dict?.set(key, keyValue); vm.executeHint(hint); diff --git a/src/hints/getCurrentAccessIndex.ts b/src/hints/getCurrentAccessIndex.ts new file mode 100644 index 00000000..66db4878 --- /dev/null +++ b/src/hints/getCurrentAccessIndex.ts @@ -0,0 +1,34 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { resOp, ResOp } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; + +/** Zod object to parse GetCurrentAccessIndex hint */ +export const getCurrentAccessIndexParser = z + .object({ GetCurrentAccessIndex: z.object({ range_check_ptr: resOp }) }) + .transform(({ GetCurrentAccessIndex: { range_check_ptr } }) => ({ + type: HintName.GetCurrentAccessIndex, + rangeCheckPtr: range_check_ptr, + })); + +/** + * GetCurrentAccessIndex hint + * + */ +export type GetCurrentAccessIndexParser = z.infer< + typeof getCurrentAccessIndexParser +>; + +/** + * Assert that the biggest key of the squashed dictionnary is < 2 ** 128 + */ +export const getCurrentAccessIndex = ( + vm: VirtualMachine, + rangeCheckPtr: ResOp +) => { + vm.memory.assertEq( + vm.getPointer(...vm.extractBuffer(rangeCheckPtr)), + vm.squashedDictManager.lastIndex() + ); +}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index 7ebc13ae..4d9d8a2a 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -7,4 +7,5 @@ export enum HintName { Felt252DictEntryUpdate = 'Felt252DictEntryUpdate', GetSegmentArenaIndex = 'GetSegmentArenaIndex', InitSquashData = 'InitSquashData', + GetCurrentAccessIndex = 'GetCurrentAccessIndex', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 75902d43..8f1b93e3 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -7,6 +7,7 @@ import { getSegmentArenaIndexParser } from './getSegmentArenaIndex'; import { felt252DictEntryInitParser } from './felt252DictEntryInit'; import { felt252DictEntryUpdateParser } from './felt252DictEntryUpdate'; import { initSquashDataParser } from './initSquashData'; +import { getCurrentAccessIndexParser } from './getCurrentAccessIndex'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -17,6 +18,7 @@ const hint = z.union([ felt252DictEntryInitParser, felt252DictEntryUpdateParser, initSquashDataParser, + getCurrentAccessIndexParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index d5555fe5..0156ab36 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -59,6 +59,10 @@ import { } from 'hints/felt252DictEntryUpdate'; import { initSquashData, InitSquashData } from 'hints/initSquashData'; import { UndefinedSegmentValue } from 'errors/memory'; +import { + getCurrentAccessIndex, + GetCurrentAccessIndexParser, +} from 'hints/getCurrentAccessIndex'; export type TraceEntry = { pc: Relocatable; @@ -124,6 +128,7 @@ export class VirtualMachine { const h = hint as Felt252DictEntryUpdate; felt252DictEntryUpdate(vm, h.dictPtr, h.value); }, + [HintName.InitSquashData]: (vm, hint) => { const h = hint as InitSquashData; initSquashData( @@ -135,6 +140,10 @@ export class VirtualMachine { h.firstKey ); }, + [HintName.GetCurrentAccessIndex]: (vm, hint) => { + const h = hint as GetCurrentAccessIndexParser; + getCurrentAccessIndex(vm, h.rangeCheckPtr); + }, }; constructor() { @@ -652,7 +661,7 @@ export class VirtualMachine { switch (binOp.op) { case Operation.Add: - return a.add(b); + return a.add(b) as Felt; case Operation.Mul: return a.mul(b); From c28b290d4e72089c6eb07f92abb140ec4c24af5d Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 08:49:34 +0200 Subject: [PATCH 22/78] feat: add ShouldSkipSquashLoop hint --- src/hints/getCurrentAccessIndex.ts | 4 +--- src/hints/hintName.ts | 1 + src/hints/hintSchema.ts | 2 ++ src/hints/shouldSkipSquashLoop.ts | 37 ++++++++++++++++++++++++++++++ src/vm/virtualMachine.ts | 12 ++++++++-- 5 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 src/hints/shouldSkipSquashLoop.ts diff --git a/src/hints/getCurrentAccessIndex.ts b/src/hints/getCurrentAccessIndex.ts index 66db4878..95d23293 100644 --- a/src/hints/getCurrentAccessIndex.ts +++ b/src/hints/getCurrentAccessIndex.ts @@ -16,9 +16,7 @@ export const getCurrentAccessIndexParser = z * GetCurrentAccessIndex hint * */ -export type GetCurrentAccessIndexParser = z.infer< - typeof getCurrentAccessIndexParser ->; +export type GetCurrentAccessIndex = z.infer; /** * Assert that the biggest key of the squashed dictionnary is < 2 ** 128 diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index 4d9d8a2a..e27d679c 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -8,4 +8,5 @@ export enum HintName { GetSegmentArenaIndex = 'GetSegmentArenaIndex', InitSquashData = 'InitSquashData', GetCurrentAccessIndex = 'GetCurrentAccessIndex', + ShouldSkipSquashLoop = 'ShouldSkipSquashLoop', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 8f1b93e3..b316c9c2 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -8,6 +8,7 @@ import { felt252DictEntryInitParser } from './felt252DictEntryInit'; import { felt252DictEntryUpdateParser } from './felt252DictEntryUpdate'; import { initSquashDataParser } from './initSquashData'; import { getCurrentAccessIndexParser } from './getCurrentAccessIndex'; +import { shouldSkipSquashLoopParser } from './shouldSkipSquashLoop'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -19,6 +20,7 @@ const hint = z.union([ felt252DictEntryUpdateParser, initSquashDataParser, getCurrentAccessIndexParser, + shouldSkipSquashLoopParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/hints/shouldSkipSquashLoop.ts b/src/hints/shouldSkipSquashLoop.ts new file mode 100644 index 00000000..8e745fb0 --- /dev/null +++ b/src/hints/shouldSkipSquashLoop.ts @@ -0,0 +1,37 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; + +/** Zod object to parse ShouldSkipSquashLoop hint */ +export const shouldSkipSquashLoopParser = z + .object({ ShouldSkipSquashLoop: z.object({ should_skip_loop: cellRef }) }) + .transform(({ ShouldSkipSquashLoop: { should_skip_loop } }) => ({ + type: HintName.ShouldSkipSquashLoop, + shouldSkipLoop: should_skip_loop, + })); + +/** + * ShouldSkipSquashLoop hint + * + * Check whether the squash loop should be skipped. + */ +export type ShouldSkipSquashLoop = z.infer; + +/** + * Check whether the squash loop should be skipped. + * + * If there is still indices to be squashed, the loop is skipped. + */ +export const shouldSkipSquashLoop = ( + vm: VirtualMachine, + shouldSkipLoop: CellRef +) => { + const flag = vm.squashedDictManager.lastIndices().length + ? new Felt(1n) + : new Felt(0n); + + vm.memory.assertEq(vm.cellRefToRelocatable(shouldSkipLoop), flag); +}; diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 0156ab36..80078e60 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -61,8 +61,12 @@ import { initSquashData, InitSquashData } from 'hints/initSquashData'; import { UndefinedSegmentValue } from 'errors/memory'; import { getCurrentAccessIndex, - GetCurrentAccessIndexParser, + GetCurrentAccessIndex, } from 'hints/getCurrentAccessIndex'; +import { + shouldSkipSquashLoop, + ShouldSkipSquashLoop, +} from 'hints/shouldSkipSquashLoop'; export type TraceEntry = { pc: Relocatable; @@ -141,9 +145,13 @@ export class VirtualMachine { ); }, [HintName.GetCurrentAccessIndex]: (vm, hint) => { - const h = hint as GetCurrentAccessIndexParser; + const h = hint as GetCurrentAccessIndex; getCurrentAccessIndex(vm, h.rangeCheckPtr); }, + [HintName.ShouldSkipSquashLoop]: (vm, hint) => { + const h = hint as ShouldSkipSquashLoop; + shouldSkipSquashLoop(vm, h.shouldSkipLoop); + }, }; constructor() { From 5a7422c2a3d36fe1a07350934f3f6fdc1a311e26 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 11:12:30 +0200 Subject: [PATCH 23/78] feat: add GetCurrentAccessDelta hint --- src/hints/getCurrentAccessDelta.ts | 38 +++++++++++++++++++++++++ src/hints/hintName.ts | 2 ++ src/hints/hintSchema.ts | 4 +++ src/hints/shouldContinueSquashLoop.ts | 40 +++++++++++++++++++++++++++ src/hints/shouldSkipSquashLoop.ts | 7 +++-- src/vm/virtualMachine.ts | 18 +++++++++++- 6 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 src/hints/getCurrentAccessDelta.ts create mode 100644 src/hints/shouldContinueSquashLoop.ts diff --git a/src/hints/getCurrentAccessDelta.ts b/src/hints/getCurrentAccessDelta.ts new file mode 100644 index 00000000..cf7d55e7 --- /dev/null +++ b/src/hints/getCurrentAccessDelta.ts @@ -0,0 +1,38 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; + +/** Zod object to parse GetCurrentAccessDelta hint */ +export const getCurrentAccessDeltaParser = z + .object({ GetCurrentAccessDelta: z.object({ index_delta_minus1: cellRef }) }) + .transform(({ GetCurrentAccessDelta: { index_delta_minus1 } }) => ({ + type: HintName.GetCurrentAccessDelta, + indexDeltaMinusOne: index_delta_minus1, + })); + +/** + * GetCurrentAccessDelta hint + * + * Assert to memory the difference between the current and previous indices. + * + */ +export type GetCurrentAccessDelta = z.infer; + +/** + * Assert `currIndex - prevIndex - 1` to `indexDeltaMinusOne` + */ +export const getCurrentAccessDelta = ( + vm: VirtualMachine, + indexDeltaMinusOne: CellRef +) => { + const prevIndex = vm.squashedDictManager.popIndex(); + const currIndex = vm.squashedDictManager.lastIndex(); + + vm.memory.assertEq( + vm.cellRefToRelocatable(indexDeltaMinusOne), + currIndex.sub(prevIndex).sub(new Felt(1n)) + ); +}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index e27d679c..decadb73 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -9,4 +9,6 @@ export enum HintName { InitSquashData = 'InitSquashData', GetCurrentAccessIndex = 'GetCurrentAccessIndex', ShouldSkipSquashLoop = 'ShouldSkipSquashLoop', + GetCurrentAccessDelta = 'GetCurrentAccessDelta', + ShouldContinueSquashLoop = 'ShouldContinueSquashLoop', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index b316c9c2..3d5e8c48 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -9,6 +9,8 @@ import { felt252DictEntryUpdateParser } from './felt252DictEntryUpdate'; import { initSquashDataParser } from './initSquashData'; import { getCurrentAccessIndexParser } from './getCurrentAccessIndex'; import { shouldSkipSquashLoopParser } from './shouldSkipSquashLoop'; +import { getCurrentAccessDeltaParser } from './getCurrentAccessDelta'; +import { shouldContinueSquashLoopParser } from './shouldContinueSquashLoop'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -21,6 +23,8 @@ const hint = z.union([ initSquashDataParser, getCurrentAccessIndexParser, shouldSkipSquashLoopParser, + getCurrentAccessDeltaParser, + shouldContinueSquashLoopParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/hints/shouldContinueSquashLoop.ts b/src/hints/shouldContinueSquashLoop.ts new file mode 100644 index 00000000..a6396501 --- /dev/null +++ b/src/hints/shouldContinueSquashLoop.ts @@ -0,0 +1,40 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; + +/** Zod object to parse ShouldContinueSquashLoop hint */ +export const shouldContinueSquashLoopParser = z + .object({ ShouldContinueSquashLoop: z.object({ should_continue: cellRef }) }) + .transform(({ ShouldContinueSquashLoop: { should_continue } }) => ({ + type: HintName.ShouldContinueSquashLoop, + shouldContinue: should_continue, + })); + +/** + * ShouldContinueSquashLoop hint + * + * Check whether the squash loop should be skipped. + */ +export type ShouldContinueSquashLoop = z.infer< + typeof shouldContinueSquashLoopParser +>; + +/** + * Check whether the squash loop should be skipped. + * + * If there is still indices to be squashed, the loop is skipped. + */ +export const shouldContinueSquashLoop = ( + vm: VirtualMachine, + shouldContinue: CellRef +) => { + const flag = + vm.squashedDictManager.lastIndices().length <= 1 + ? new Felt(1n) + : new Felt(0n); + + vm.memory.assertEq(vm.cellRefToRelocatable(shouldContinue), flag); +}; diff --git a/src/hints/shouldSkipSquashLoop.ts b/src/hints/shouldSkipSquashLoop.ts index 8e745fb0..932ef27d 100644 --- a/src/hints/shouldSkipSquashLoop.ts +++ b/src/hints/shouldSkipSquashLoop.ts @@ -29,9 +29,10 @@ export const shouldSkipSquashLoop = ( vm: VirtualMachine, shouldSkipLoop: CellRef ) => { - const flag = vm.squashedDictManager.lastIndices().length - ? new Felt(1n) - : new Felt(0n); + const flag = + vm.squashedDictManager.lastIndices().length > 1 + ? new Felt(0n) + : new Felt(1n); vm.memory.assertEq(vm.cellRefToRelocatable(shouldSkipLoop), flag); }; diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 80078e60..1aa374a4 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -1,4 +1,5 @@ import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; +import { UndefinedSegmentValue } from 'errors/memory'; import { InvalidDst, InvalidOp1, @@ -58,7 +59,6 @@ import { felt252DictEntryUpdate, } from 'hints/felt252DictEntryUpdate'; import { initSquashData, InitSquashData } from 'hints/initSquashData'; -import { UndefinedSegmentValue } from 'errors/memory'; import { getCurrentAccessIndex, GetCurrentAccessIndex, @@ -67,6 +67,14 @@ import { shouldSkipSquashLoop, ShouldSkipSquashLoop, } from 'hints/shouldSkipSquashLoop'; +import { + GetCurrentAccessDelta, + getCurrentAccessDelta, +} from 'hints/getCurrentAccessDelta'; +import { + shouldContinueSquashLoop, + ShouldContinueSquashLoop, +} from 'hints/shouldContinueSquashLoop'; export type TraceEntry = { pc: Relocatable; @@ -152,6 +160,14 @@ export class VirtualMachine { const h = hint as ShouldSkipSquashLoop; shouldSkipSquashLoop(vm, h.shouldSkipLoop); }, + [HintName.GetCurrentAccessDelta]: (vm, hint) => { + const h = hint as GetCurrentAccessDelta; + getCurrentAccessDelta(vm, h.indexDeltaMinusOne); + }, + [HintName.ShouldContinueSquashLoop]: (vm, hint) => { + const h = hint as ShouldContinueSquashLoop; + shouldContinueSquashLoop(vm, h.shouldContinue); + }, }; constructor() { From 28292e9b9e36d64372521b6a00453abdbf5a5da8 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 12:01:21 +0200 Subject: [PATCH 24/78] feat: add GetNextDictKey hint --- src/hints/getNextDictKey.ts | 29 +++++++++++++++++++++++++++++ src/hints/hintName.ts | 1 + src/hints/hintSchema.ts | 2 ++ src/vm/virtualMachine.ts | 5 +++++ 4 files changed, 37 insertions(+) create mode 100644 src/hints/getNextDictKey.ts diff --git a/src/hints/getNextDictKey.ts b/src/hints/getNextDictKey.ts new file mode 100644 index 00000000..f0c2b109 --- /dev/null +++ b/src/hints/getNextDictKey.ts @@ -0,0 +1,29 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; + +/** Zod object to parse GetNextDictKey hint */ +export const getNextDictKeyParser = z + .object({ GetNextDictKey: z.object({ next_key: cellRef }) }) + .transform(({ GetNextDictKey: { next_key } }) => ({ + type: HintName.GetNextDictKey, + nextKey: next_key, + })); + +/** + * GetNextDictKey hint + */ +export type GetNextDictKey = z.infer; + +/** + * Pop the current key and assert the next one to `nextKey` + */ +export const getNextDictKey = (vm: VirtualMachine, nextKey: CellRef) => { + vm.squashedDictManager.popKey(); + vm.memory.assertEq( + vm.cellRefToRelocatable(nextKey), + vm.squashedDictManager.lastKey() + ); +}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index decadb73..5673937d 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -11,4 +11,5 @@ export enum HintName { ShouldSkipSquashLoop = 'ShouldSkipSquashLoop', GetCurrentAccessDelta = 'GetCurrentAccessDelta', ShouldContinueSquashLoop = 'ShouldContinueSquashLoop', + GetNextDictKey = 'GetNextDictKey', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 3d5e8c48..07d81947 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -11,6 +11,7 @@ import { getCurrentAccessIndexParser } from './getCurrentAccessIndex'; import { shouldSkipSquashLoopParser } from './shouldSkipSquashLoop'; import { getCurrentAccessDeltaParser } from './getCurrentAccessDelta'; import { shouldContinueSquashLoopParser } from './shouldContinueSquashLoop'; +import { getNextDictKeyParser } from './getNextDictKey'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -25,6 +26,7 @@ const hint = z.union([ shouldSkipSquashLoopParser, getCurrentAccessDeltaParser, shouldContinueSquashLoopParser, + getNextDictKeyParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 1aa374a4..aa3c7078 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -75,6 +75,7 @@ import { shouldContinueSquashLoop, ShouldContinueSquashLoop, } from 'hints/shouldContinueSquashLoop'; +import { getNextDictKey, GetNextDictKey } from 'hints/getNextDictKey'; export type TraceEntry = { pc: Relocatable; @@ -168,6 +169,10 @@ export class VirtualMachine { const h = hint as ShouldContinueSquashLoop; shouldContinueSquashLoop(vm, h.shouldContinue); }, + [HintName.GetNextDictKey]: (vm, hint) => { + const h = hint as GetNextDictKey; + getNextDictKey(vm, h.nextKey); + }, }; constructor() { From 1012ea93a5d4d0acb252cbf94857dfee98596974 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 14:05:01 +0200 Subject: [PATCH 25/78] feat: add Arc-related hints --- cairo_programs/cairo/hints/dict_insert.cairo | 2 + src/hints/assertLeFindSmallArc.ts | 61 ++++++++++++++++++++ src/hints/assertLeIsFirstArcExcluded.ts | 38 ++++++++++++ src/hints/assertLeIsSecondArcExcluded.ts | 38 ++++++++++++ src/hints/hintName.ts | 3 + src/hints/hintSchema.ts | 6 ++ src/primitives/felt.ts | 5 ++ src/vm/virtualMachine.ts | 24 ++++++++ 8 files changed, 177 insertions(+) create mode 100644 src/hints/assertLeFindSmallArc.ts create mode 100644 src/hints/assertLeIsFirstArcExcluded.ts create mode 100644 src/hints/assertLeIsSecondArcExcluded.ts diff --git a/cairo_programs/cairo/hints/dict_insert.cairo b/cairo_programs/cairo/hints/dict_insert.cairo index abc66d10..1a90b059 100644 --- a/cairo_programs/cairo/hints/dict_insert.cairo +++ b/cairo_programs/cairo/hints/dict_insert.cairo @@ -1,4 +1,6 @@ fn main() { let mut balances: Felt252Dict = Default::default(); balances.insert('Simon', 100); + balances.insert('Alice', 500); + balances.insert('Bob', 30); } diff --git a/src/hints/assertLeFindSmallArc.ts b/src/hints/assertLeFindSmallArc.ts new file mode 100644 index 00000000..9daf031e --- /dev/null +++ b/src/hints/assertLeFindSmallArc.ts @@ -0,0 +1,61 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { ResOp, resOp } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; + +/** Zod object to parse AssertLeFindSmallArcs hint */ +export const assertLeFindSmallArcsParser = z + .object({ + AssertLeFindSmallArcs: z.object({ + range_check_ptr: resOp, + a: resOp, + b: resOp, + }), + }) + .transform(({ AssertLeFindSmallArcs: { range_check_ptr, a, b } }) => ({ + type: HintName.AssertLeFindSmallArcs, + rangeCheckPtr: range_check_ptr, + a, + b, + })); + +/** + * AssertLeFindSmallArcs hint + */ +export type AssertLeFindSmallArcs = z.infer; + +export type Arc = { + value: Felt; + pos: number; +}; + +/** + * Check whether the first arc from `` is excluded. + */ +export const assertLeFindSmallArcs = ( + vm: VirtualMachine, + rangeCheckPtr: ResOp, + a: ResOp, + b: ResOp +) => { + const aVal = vm.getResOperandValue(a); + const bVal = vm.getResOperandValue(b); + const arcs: Arc[] = [ + { value: aVal, pos: 0 }, + { value: bVal.sub(aVal), pos: 1 }, + { value: new Felt(-1n).sub(bVal), pos: 2 }, + ].sort((a, b) => (a.value > b.value ? 1 : a.value < b.value ? -1 : 0)); + + vm.scopeManager.set('excluded_arc', arcs[2].pos); + + const primeOver3High = new Felt(3544607988759775765608368578435044694n); + const primeOver2High = new Felt(5316911983139663648412552867652567041n); + const ptr = vm.getPointer(...vm.extractBuffer(rangeCheckPtr)); + + vm.memory.assertEq(ptr, arcs[0].value.mod(primeOver3High)); + vm.memory.assertEq(ptr, arcs[0].value.div(primeOver3High)); + vm.memory.assertEq(ptr, arcs[1].value.mod(primeOver2High)); + vm.memory.assertEq(ptr, arcs[1].value.div(primeOver2High)); +}; diff --git a/src/hints/assertLeIsFirstArcExcluded.ts b/src/hints/assertLeIsFirstArcExcluded.ts new file mode 100644 index 00000000..f8b76829 --- /dev/null +++ b/src/hints/assertLeIsFirstArcExcluded.ts @@ -0,0 +1,38 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; +import { Arc } from './assertLeFindSmallArc'; + +/** Zod object to parse AssertLeIsFirstArcExcluded hint */ +export const assertLeIsFirstArcExcludedParser = z + .object({ + AssertLeIsFirstArcExcluded: z.object({ skip_exclude_a_flag: cellRef }), + }) + .transform(({ AssertLeIsFirstArcExcluded: { skip_exclude_a_flag } }) => ({ + type: HintName.AssertLeIsFirstArcExcluded, + skipExcludeFirstArc: skip_exclude_a_flag, + })); + +/** + * AssertLeIsFirstArcExcluded hint + */ +export type AssertLeIsFirstArcExcluded = z.infer< + typeof assertLeIsFirstArcExcludedParser +>; + +/** + * Check whether the first arc from `` is excluded. + */ +export const assertLeIsFirstArcExcluded = ( + vm: VirtualMachine, + skipExcludeFirstArc: CellRef +) => { + const excludedArc = vm.scopeManager.get('excluded_arc'); + vm.memory.assertEq( + vm.cellRefToRelocatable(skipExcludeFirstArc), + (excludedArc as Arc).pos != 0 ? new Felt(1n) : new Felt(0n) + ); +}; diff --git a/src/hints/assertLeIsSecondArcExcluded.ts b/src/hints/assertLeIsSecondArcExcluded.ts new file mode 100644 index 00000000..ab01dbc8 --- /dev/null +++ b/src/hints/assertLeIsSecondArcExcluded.ts @@ -0,0 +1,38 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; +import { Arc } from './assertLeFindSmallArc'; + +/** Zod object to parse AssertLeIsSecondArcExcluded hint */ +export const assertLeIsSecondArcExcludedParser = z + .object({ + AssertLeIsSecondArcExcluded: z.object({ skip_exclude_b_minus_a: cellRef }), + }) + .transform(({ AssertLeIsSecondArcExcluded: { skip_exclude_b_minus_a } }) => ({ + type: HintName.AssertLeIsSecondArcExcluded, + skipExcludeSecondArc: skip_exclude_b_minus_a, + })); + +/** + * AssertLeIsSecondArcExcluded hint + */ +export type AssertLeIsSecondArcExcluded = z.infer< + typeof assertLeIsSecondArcExcludedParser +>; + +/** + * Check whether the first arc from `` is excluded. + */ +export const assertLeIsSecondArcExcluded = ( + vm: VirtualMachine, + skipExcludeAFlag: CellRef +) => { + const excludedArc = vm.scopeManager.get('excluded_arc'); + vm.memory.assertEq( + vm.cellRefToRelocatable(skipExcludeAFlag), + (excludedArc as Arc).pos != 1 ? new Felt(1n) : new Felt(0n) + ); +}; diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index 5673937d..6777fcf0 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -12,4 +12,7 @@ export enum HintName { GetCurrentAccessDelta = 'GetCurrentAccessDelta', ShouldContinueSquashLoop = 'ShouldContinueSquashLoop', GetNextDictKey = 'GetNextDictKey', + AssertLeFindSmallArcs = 'AssertLeFindSmallArcs', + AssertLeIsFirstArcExcluded = 'AssertLeIsFirstArcExcluded', + AssertLeIsSecondArcExcluded = 'AssertLeIsSecondArcExcluded', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 07d81947..ecfc6e8b 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -12,6 +12,9 @@ import { shouldSkipSquashLoopParser } from './shouldSkipSquashLoop'; import { getCurrentAccessDeltaParser } from './getCurrentAccessDelta'; import { shouldContinueSquashLoopParser } from './shouldContinueSquashLoop'; import { getNextDictKeyParser } from './getNextDictKey'; +import { assertLeIsFirstArcExcludedParser } from './assertLeIsFirstArcExcluded'; +import { assertLeFindSmallArcsParser } from './assertLeFindSmallArc'; +import { assertLeIsSecondArcExcludedParser } from './assertLeIsSecondArcExcluded'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -27,6 +30,9 @@ const hint = z.union([ getCurrentAccessDeltaParser, shouldContinueSquashLoopParser, getNextDictKeyParser, + assertLeFindSmallArcsParser, + assertLeIsFirstArcExcludedParser, + assertLeIsSecondArcExcludedParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/primitives/felt.ts b/src/primitives/felt.ts index f33e0716..558c041c 100644 --- a/src/primitives/felt.ts +++ b/src/primitives/felt.ts @@ -77,6 +77,11 @@ export class Felt { return this.mul(other.inv()); } + mod(other: SegmentValue): Felt { + if (!isFelt(other)) throw new ExpectedFelt(other); + return new Felt(this.inner % other.inner); + } + eq(other: SegmentValue): boolean { return !isRelocatable(other) && this.inner === other.inner; } diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index aa3c7078..4641733d 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -76,6 +76,18 @@ import { ShouldContinueSquashLoop, } from 'hints/shouldContinueSquashLoop'; import { getNextDictKey, GetNextDictKey } from 'hints/getNextDictKey'; +import { + assertLeFindSmallArcs, + AssertLeFindSmallArcs, +} from 'hints/assertLeFindSmallArc'; +import { + assertLeIsFirstArcExcluded, + AssertLeIsFirstArcExcluded, +} from 'hints/assertLeIsFirstArcExcluded'; +import { + assertLeIsSecondArcExcluded, + AssertLeIsSecondArcExcluded, +} from 'hints/assertLeIsSecondArcExcluded'; export type TraceEntry = { pc: Relocatable; @@ -173,6 +185,18 @@ export class VirtualMachine { const h = hint as GetNextDictKey; getNextDictKey(vm, h.nextKey); }, + [HintName.AssertLeFindSmallArcs]: (vm, hint) => { + const h = hint as AssertLeFindSmallArcs; + assertLeFindSmallArcs(vm, h.rangeCheckPtr, h.a, h.b); + }, + [HintName.AssertLeIsFirstArcExcluded]: (vm, hint) => { + const h = hint as AssertLeIsFirstArcExcluded; + assertLeIsFirstArcExcluded(vm, h.skipExcludeFirstArc); + }, + [HintName.AssertLeIsSecondArcExcluded]: (vm, hint) => { + const h = hint as AssertLeIsSecondArcExcluded; + assertLeIsSecondArcExcluded(vm, h.skipExcludeSecondArc); + }, }; constructor() { From 5d388092afdf07b678fbe71bd858e105724d3d69 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 14:44:46 +0200 Subject: [PATCH 26/78] doc: add JSDoc to Arc-related hints --- src/hints/assertLeFindSmallArc.ts | 9 ++++++++- src/hints/assertLeIsFirstArcExcluded.ts | 4 +++- src/hints/assertLeIsSecondArcExcluded.ts | 8 +++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/hints/assertLeFindSmallArc.ts b/src/hints/assertLeFindSmallArc.ts index 9daf031e..00607641 100644 --- a/src/hints/assertLeFindSmallArc.ts +++ b/src/hints/assertLeFindSmallArc.ts @@ -32,7 +32,12 @@ export type Arc = { }; /** - * Check whether the first arc from `` is excluded. + * Compute the three arcs `a`, `b - a` and `PRIME - 1 - b` + * + * Set the biggest arc to the scope variable `excluded_arc` + * + * Store the modulo and remainder of the smallest arc (resp. second smallest arc) + * against `Math.ceil(Felt.PRIME / 3) >> 128n` (resp. `Math.ceil(Felt.PRIME / 2) >> 128n`) */ export const assertLeFindSmallArcs = ( vm: VirtualMachine, @@ -52,6 +57,8 @@ export const assertLeFindSmallArcs = ( const primeOver3High = new Felt(3544607988759775765608368578435044694n); const primeOver2High = new Felt(5316911983139663648412552867652567041n); + if (!primeOver3High.eq(new Felt((Felt.PRIME / 3n) >> 127n))) + throw new Error('Felt over 3 is not equal to the given expression'); const ptr = vm.getPointer(...vm.extractBuffer(rangeCheckPtr)); vm.memory.assertEq(ptr, arcs[0].value.mod(primeOver3High)); diff --git a/src/hints/assertLeIsFirstArcExcluded.ts b/src/hints/assertLeIsFirstArcExcluded.ts index f8b76829..746f46c1 100644 --- a/src/hints/assertLeIsFirstArcExcluded.ts +++ b/src/hints/assertLeIsFirstArcExcluded.ts @@ -24,7 +24,9 @@ export type AssertLeIsFirstArcExcluded = z.infer< >; /** - * Check whether the first arc from `` is excluded. + * Check whether the first arc from `AssertLeFindSmallArcs` is excluded. + * + * Read the value in scope at `excluded_arc` */ export const assertLeIsFirstArcExcluded = ( vm: VirtualMachine, diff --git a/src/hints/assertLeIsSecondArcExcluded.ts b/src/hints/assertLeIsSecondArcExcluded.ts index ab01dbc8..884d2a0c 100644 --- a/src/hints/assertLeIsSecondArcExcluded.ts +++ b/src/hints/assertLeIsSecondArcExcluded.ts @@ -24,15 +24,17 @@ export type AssertLeIsSecondArcExcluded = z.infer< >; /** - * Check whether the first arc from `` is excluded. + * Check whether the second arc from `AssertLeFindSmallArcs` is excluded. + * + * Read the value in scope at `excluded_arc` */ export const assertLeIsSecondArcExcluded = ( vm: VirtualMachine, - skipExcludeAFlag: CellRef + skipExcludeSecondArc: CellRef ) => { const excludedArc = vm.scopeManager.get('excluded_arc'); vm.memory.assertEq( - vm.cellRefToRelocatable(skipExcludeAFlag), + vm.cellRefToRelocatable(skipExcludeSecondArc), (excludedArc as Arc).pos != 1 ? new Felt(1n) : new Felt(0n) ); }; From 8236c848f544e5adec424fb2b5ce1898664fc84a Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 14:44:50 +0200 Subject: [PATCH 27/78] chore: add Cairo program with two distinc dicts --- cairo_programs/cairo/hints/multiple_dict.cairo | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 cairo_programs/cairo/hints/multiple_dict.cairo diff --git a/cairo_programs/cairo/hints/multiple_dict.cairo b/cairo_programs/cairo/hints/multiple_dict.cairo new file mode 100644 index 00000000..8966c0b3 --- /dev/null +++ b/cairo_programs/cairo/hints/multiple_dict.cairo @@ -0,0 +1,6 @@ +fn main() { + let mut balances_1: Felt252Dict = Default::default(); + let mut balances_2: Felt252Dict = Default::default(); + balances_1.insert('Alice', 500); + balances_2.insert('Bob', 30); +} From c5c420d4c4e2bace64943147a58fba25b5bc7a3c Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 18:09:26 +0200 Subject: [PATCH 28/78] fix: fix condition ShouldContinueSquashLoop hint --- src/hints/shouldContinueSquashLoop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hints/shouldContinueSquashLoop.ts b/src/hints/shouldContinueSquashLoop.ts index a6396501..bb8e4c25 100644 --- a/src/hints/shouldContinueSquashLoop.ts +++ b/src/hints/shouldContinueSquashLoop.ts @@ -32,7 +32,7 @@ export const shouldContinueSquashLoop = ( shouldContinue: CellRef ) => { const flag = - vm.squashedDictManager.lastIndices().length <= 1 + vm.squashedDictManager.lastIndices().length > 1 ? new Felt(1n) : new Felt(0n); From fd6a1bf098d163d6885e6270f997f6cb02a174e5 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 18:25:31 +0200 Subject: [PATCH 29/78] refactor: use Map with primitive types as key for dict managers --- cairo_programs/cairo/hints/dict_multiple_insert.cairo | 7 +++++++ cairo_programs/cairo/hints/dict_squash.cairo | 7 +++++++ src/hints/assertLeFindSmallArc.ts | 2 -- src/hints/felt252DictEntryInit.test.ts | 4 ++-- src/hints/felt252DictEntryInit.ts | 2 +- src/hints/felt252DictEntryUpdate.test.ts | 2 +- src/hints/felt252DictEntryUpdate.ts | 2 +- src/hints/initSquashData.ts | 2 +- src/vm/squashedDict.ts | 11 ++++++----- src/vm/virtualMachine.test.ts | 2 +- src/vm/virtualMachine.ts | 6 +++--- 11 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 cairo_programs/cairo/hints/dict_multiple_insert.cairo create mode 100644 cairo_programs/cairo/hints/dict_squash.cairo diff --git a/cairo_programs/cairo/hints/dict_multiple_insert.cairo b/cairo_programs/cairo/hints/dict_multiple_insert.cairo new file mode 100644 index 00000000..44bf6628 --- /dev/null +++ b/cairo_programs/cairo/hints/dict_multiple_insert.cairo @@ -0,0 +1,7 @@ +fn main() { + let mut balances: Felt252Dict = Default::default(); + balances.insert('Simon', 100); + balances.insert('Simon', 500); + balances.insert('Simon', 600); + balances.insert('Simon', 700); +} diff --git a/cairo_programs/cairo/hints/dict_squash.cairo b/cairo_programs/cairo/hints/dict_squash.cairo new file mode 100644 index 00000000..6778ac17 --- /dev/null +++ b/cairo_programs/cairo/hints/dict_squash.cairo @@ -0,0 +1,7 @@ +fn main() { + let mut dict = felt252_dict_new::(); + dict.insert(1, 64); + dict.insert(2, 75); + dict.insert(3, 75); + dict.squash(); +} diff --git a/src/hints/assertLeFindSmallArc.ts b/src/hints/assertLeFindSmallArc.ts index 00607641..5e00277b 100644 --- a/src/hints/assertLeFindSmallArc.ts +++ b/src/hints/assertLeFindSmallArc.ts @@ -57,8 +57,6 @@ export const assertLeFindSmallArcs = ( const primeOver3High = new Felt(3544607988759775765608368578435044694n); const primeOver2High = new Felt(5316911983139663648412552867652567041n); - if (!primeOver3High.eq(new Felt((Felt.PRIME / 3n) >> 127n))) - throw new Error('Felt over 3 is not equal to the given expression'); const ptr = vm.getPointer(...vm.extractBuffer(rangeCheckPtr)); vm.memory.assertEq(ptr, arcs[0].value.mod(primeOver3High)); diff --git a/src/hints/felt252DictEntryInit.test.ts b/src/hints/felt252DictEntryInit.test.ts index 79adb981..40cb4ab9 100644 --- a/src/hints/felt252DictEntryInit.test.ts +++ b/src/hints/felt252DictEntryInit.test.ts @@ -91,12 +91,12 @@ describe('Felt252DictEntryInit', () => { expect(dict).not.toBeUndefined(); const keyValue = new Felt(13n); - dict?.set(key, keyValue); + dict?.set(key.toString(), keyValue); vm.executeHint(hint); if (dict) { - expect(dict.get(key)).toEqual(new Felt(13n)); + expect(dict.get(key.toString())).toEqual(new Felt(13n)); expect(vm.memory.get(newDictPtr.add(1))).toEqual(keyValue); } }); diff --git a/src/hints/felt252DictEntryInit.ts b/src/hints/felt252DictEntryInit.ts index b047450c..80c7c3e9 100644 --- a/src/hints/felt252DictEntryInit.ts +++ b/src/hints/felt252DictEntryInit.ts @@ -42,7 +42,7 @@ export const felt252DictEntryInit = ( key: ResOp ) => { const address = vm.getPointer(...vm.extractBuffer(dictPtr)); - const keyValue = vm.getResOperandValue(key); + const keyValue = vm.getResOperandValue(key).toString(); const dict = vm.getDict(address); const prevValue = dict.get(keyValue) || new Felt(0n); dict.set(keyValue, prevValue); diff --git a/src/hints/felt252DictEntryUpdate.test.ts b/src/hints/felt252DictEntryUpdate.test.ts index 2b8d777b..f476b6e3 100644 --- a/src/hints/felt252DictEntryUpdate.test.ts +++ b/src/hints/felt252DictEntryUpdate.test.ts @@ -99,7 +99,7 @@ describe('Felt252DictEntryUpdate', () => { vm.executeHint(hint); if (dict) { - expect(dict.get(keyValue)).toEqual(value); + expect(dict.get(keyValue.toString())).toEqual(value); } } ); diff --git a/src/hints/felt252DictEntryUpdate.ts b/src/hints/felt252DictEntryUpdate.ts index 63456a29..275ff26a 100644 --- a/src/hints/felt252DictEntryUpdate.ts +++ b/src/hints/felt252DictEntryUpdate.ts @@ -49,5 +49,5 @@ export const felt252DictEntryUpdate = ( value.type === OpType.Deref ? vm.getSegmentValue((value as Deref).cell) : vm.getResOperandValue(value); - vm.getDict(address).set(key, val); + vm.getDict(address).set(key.toString(), val); }; diff --git a/src/hints/initSquashData.ts b/src/hints/initSquashData.ts index 3dc8deca..ffc5395e 100644 --- a/src/hints/initSquashData.ts +++ b/src/hints/initSquashData.ts @@ -74,7 +74,7 @@ export const initSquashData = ( } vm.squashedDictManager.keyToIndices.forEach((values, key) => { values.reverse(); - vm.squashedDictManager.keys.push(key); + vm.squashedDictManager.keys.push(new Felt(BigInt(key))); }); vm.squashedDictManager.keys.sort((a, b) => (a < b ? 1 : a > b ? -1 : 0)); vm.memory.assertEq( diff --git a/src/vm/squashedDict.ts b/src/vm/squashedDict.ts index 3d75061a..b2b37830 100644 --- a/src/vm/squashedDict.ts +++ b/src/vm/squashedDict.ts @@ -2,18 +2,19 @@ import { EmptyIndex, EmptyIndices, EmptyKeys } from 'errors/squashedDict'; import { Felt } from 'primitives/felt'; export class SquashedDictManager { - public keyToIndices: Map; + public keyToIndices: Map; public keys: Felt[]; constructor() { - this.keyToIndices = new Map(); + this.keyToIndices = new Map(); this.keys = []; } insert(key: Felt, index: Felt) { - const indices = this.keyToIndices.get(key); + const keyStr = key.toString(); + const indices = this.keyToIndices.get(keyStr); if (!indices) { - this.keyToIndices.set(key, [index]); + this.keyToIndices.set(keyStr, [index]); } else { indices.push(index); } @@ -33,7 +34,7 @@ export class SquashedDictManager { lastIndices(): Felt[] { const key = this.lastKey(); - const indices = this.keyToIndices.get(key); + const indices = this.keyToIndices.get(key.toString()); if (!indices) throw new EmptyIndices(key); return indices; } diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index d6854336..51c6cc92 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -745,7 +745,7 @@ describe('VirtualMachine', () => { const vm = new VirtualMachine(); const address = vm.newDict(); expect(address).toEqual(new Relocatable(0, 0)); - expect(vm.getDict(address)).toEqual(new Dictionnary(new Felt(BigInt(0)))); + expect(vm.getDict(address)).toEqual(new Dictionnary(new Felt(0n))); }); test('should properly set and get value of a dictionnary', () => { diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 4641733d..52ea784b 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -106,7 +106,7 @@ export type RelocatedMemory = { value: Felt; }; -export class Dictionnary extends Map { +export class Dictionnary extends Map { constructor(public readonly id: Felt) { super(); this.id = id; @@ -762,13 +762,13 @@ export class VirtualMachine { getDictValue(address: Relocatable, key: Felt): SegmentValue { const dict = this.getDict(address); - const value = dict.get(key); + const value = dict.get(key.toString()); if (!value) throw new DictValueNotFound(address, key); return value; } setDictValue(address: Relocatable, key: Felt, value: SegmentValue) { const dict = this.getDict(address); - dict.set(key, value); + dict.set(key.toString(), value); } } From 98de23b2ce923bcd98e987194eeca9114d658768 Mon Sep 17 00:00:00 2001 From: malatrax Date: Mon, 22 Jul 2024 19:01:48 +0200 Subject: [PATCH 30/78] fix: assert to the write range check address on AssertLeFindSmallArcs hint --- src/hints/assertLeFindSmallArc.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hints/assertLeFindSmallArc.ts b/src/hints/assertLeFindSmallArc.ts index 5e00277b..3dd7db30 100644 --- a/src/hints/assertLeFindSmallArc.ts +++ b/src/hints/assertLeFindSmallArc.ts @@ -60,7 +60,7 @@ export const assertLeFindSmallArcs = ( const ptr = vm.getPointer(...vm.extractBuffer(rangeCheckPtr)); vm.memory.assertEq(ptr, arcs[0].value.mod(primeOver3High)); - vm.memory.assertEq(ptr, arcs[0].value.div(primeOver3High)); - vm.memory.assertEq(ptr, arcs[1].value.mod(primeOver2High)); - vm.memory.assertEq(ptr, arcs[1].value.div(primeOver2High)); + vm.memory.assertEq(ptr.add(1), arcs[0].value.div(primeOver3High)); + vm.memory.assertEq(ptr.add(2), arcs[1].value.mod(primeOver2High)); + vm.memory.assertEq(ptr.add(3), arcs[1].value.div(primeOver2High)); }; From 9fc63af524816334cbab8cddb4bd1044d2b56fbf Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 14:20:41 +0200 Subject: [PATCH 31/78] refactor: group hints by category --- src/hints/{ => dict}/allocFelt252Dict.test.ts | 2 +- src/hints/{ => dict}/allocFelt252Dict.ts | 0 .../{ => dict}/felt252DictEntryInit.test.ts | 2 +- src/hints/{ => dict}/felt252DictEntryInit.ts | 0 .../{ => dict}/felt252DictEntryUpdate.test.ts | 2 +- .../{ => dict}/felt252DictEntryUpdate.ts | 0 src/hints/{ => dict}/getCurrentAccessDelta.ts | 0 src/hints/{ => dict}/getCurrentAccessIndex.ts | 0 src/hints/{ => dict}/getNextDictKey.ts | 0 .../{ => dict}/getSegmentArenaIndex.test.ts | 2 +- src/hints/{ => dict}/getSegmentArenaIndex.ts | 0 src/hints/{ => dict}/initSquashData.ts | 0 .../{ => dict}/shouldContinueSquashLoop.ts | 0 src/hints/{ => dict}/shouldSkipSquashLoop.ts | 0 src/hints/hintSchema.ts | 22 ++++++++-------- src/hints/{ => math}/testLessThan.test.ts | 0 src/hints/{ => math}/testLessThan.ts | 0 src/vm/virtualMachine.ts | 25 +++++++++++-------- 18 files changed, 29 insertions(+), 26 deletions(-) rename src/hints/{ => dict}/allocFelt252Dict.test.ts (97%) rename src/hints/{ => dict}/allocFelt252Dict.ts (100%) rename src/hints/{ => dict}/felt252DictEntryInit.test.ts (98%) rename src/hints/{ => dict}/felt252DictEntryInit.ts (100%) rename src/hints/{ => dict}/felt252DictEntryUpdate.test.ts (98%) rename src/hints/{ => dict}/felt252DictEntryUpdate.ts (100%) rename src/hints/{ => dict}/getCurrentAccessDelta.ts (100%) rename src/hints/{ => dict}/getCurrentAccessIndex.ts (100%) rename src/hints/{ => dict}/getNextDictKey.ts (100%) rename src/hints/{ => dict}/getSegmentArenaIndex.test.ts (98%) rename src/hints/{ => dict}/getSegmentArenaIndex.ts (100%) rename src/hints/{ => dict}/initSquashData.ts (100%) rename src/hints/{ => dict}/shouldContinueSquashLoop.ts (100%) rename src/hints/{ => dict}/shouldSkipSquashLoop.ts (100%) rename src/hints/{ => math}/testLessThan.test.ts (100%) rename src/hints/{ => math}/testLessThan.ts (100%) diff --git a/src/hints/allocFelt252Dict.test.ts b/src/hints/dict/allocFelt252Dict.test.ts similarity index 97% rename from src/hints/allocFelt252Dict.test.ts rename to src/hints/dict/allocFelt252Dict.test.ts index 06002c4d..57de7235 100644 --- a/src/hints/allocFelt252Dict.test.ts +++ b/src/hints/dict/allocFelt252Dict.test.ts @@ -4,7 +4,7 @@ import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; import { allocFelt252DictParser } from './allocFelt252Dict'; -import { OpType } from './hintParamsSchema'; +import { OpType } from '../hintParamsSchema'; import { Felt } from 'primitives/felt'; import { segmentArenaHandler } from 'builtins/segmentArena'; import { Relocatable } from 'primitives/relocatable'; diff --git a/src/hints/allocFelt252Dict.ts b/src/hints/dict/allocFelt252Dict.ts similarity index 100% rename from src/hints/allocFelt252Dict.ts rename to src/hints/dict/allocFelt252Dict.ts diff --git a/src/hints/felt252DictEntryInit.test.ts b/src/hints/dict/felt252DictEntryInit.test.ts similarity index 98% rename from src/hints/felt252DictEntryInit.test.ts rename to src/hints/dict/felt252DictEntryInit.test.ts index 40cb4ab9..6eba6dee 100644 --- a/src/hints/felt252DictEntryInit.test.ts +++ b/src/hints/dict/felt252DictEntryInit.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from 'bun:test'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { OpType } from './hintParamsSchema'; +import { OpType } from '../hintParamsSchema'; import { Felt } from 'primitives/felt'; import { segmentArenaHandler } from 'builtins/segmentArena'; import { Relocatable } from 'primitives/relocatable'; diff --git a/src/hints/felt252DictEntryInit.ts b/src/hints/dict/felt252DictEntryInit.ts similarity index 100% rename from src/hints/felt252DictEntryInit.ts rename to src/hints/dict/felt252DictEntryInit.ts diff --git a/src/hints/felt252DictEntryUpdate.test.ts b/src/hints/dict/felt252DictEntryUpdate.test.ts similarity index 98% rename from src/hints/felt252DictEntryUpdate.test.ts rename to src/hints/dict/felt252DictEntryUpdate.test.ts index f476b6e3..710a5b19 100644 --- a/src/hints/felt252DictEntryUpdate.test.ts +++ b/src/hints/dict/felt252DictEntryUpdate.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from 'bun:test'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { OpType } from './hintParamsSchema'; +import { OpType } from '../hintParamsSchema'; import { Felt } from 'primitives/felt'; import { segmentArenaHandler } from 'builtins/segmentArena'; import { Relocatable } from 'primitives/relocatable'; diff --git a/src/hints/felt252DictEntryUpdate.ts b/src/hints/dict/felt252DictEntryUpdate.ts similarity index 100% rename from src/hints/felt252DictEntryUpdate.ts rename to src/hints/dict/felt252DictEntryUpdate.ts diff --git a/src/hints/getCurrentAccessDelta.ts b/src/hints/dict/getCurrentAccessDelta.ts similarity index 100% rename from src/hints/getCurrentAccessDelta.ts rename to src/hints/dict/getCurrentAccessDelta.ts diff --git a/src/hints/getCurrentAccessIndex.ts b/src/hints/dict/getCurrentAccessIndex.ts similarity index 100% rename from src/hints/getCurrentAccessIndex.ts rename to src/hints/dict/getCurrentAccessIndex.ts diff --git a/src/hints/getNextDictKey.ts b/src/hints/dict/getNextDictKey.ts similarity index 100% rename from src/hints/getNextDictKey.ts rename to src/hints/dict/getNextDictKey.ts diff --git a/src/hints/getSegmentArenaIndex.test.ts b/src/hints/dict/getSegmentArenaIndex.test.ts similarity index 98% rename from src/hints/getSegmentArenaIndex.test.ts rename to src/hints/dict/getSegmentArenaIndex.test.ts index 05e407f7..ef1e1e7a 100644 --- a/src/hints/getSegmentArenaIndex.test.ts +++ b/src/hints/dict/getSegmentArenaIndex.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from 'bun:test'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { OpType } from './hintParamsSchema'; +import { OpType } from '../hintParamsSchema'; import { Felt } from 'primitives/felt'; import { segmentArenaHandler } from 'builtins/segmentArena'; import { Relocatable } from 'primitives/relocatable'; diff --git a/src/hints/getSegmentArenaIndex.ts b/src/hints/dict/getSegmentArenaIndex.ts similarity index 100% rename from src/hints/getSegmentArenaIndex.ts rename to src/hints/dict/getSegmentArenaIndex.ts diff --git a/src/hints/initSquashData.ts b/src/hints/dict/initSquashData.ts similarity index 100% rename from src/hints/initSquashData.ts rename to src/hints/dict/initSquashData.ts diff --git a/src/hints/shouldContinueSquashLoop.ts b/src/hints/dict/shouldContinueSquashLoop.ts similarity index 100% rename from src/hints/shouldContinueSquashLoop.ts rename to src/hints/dict/shouldContinueSquashLoop.ts diff --git a/src/hints/shouldSkipSquashLoop.ts b/src/hints/dict/shouldSkipSquashLoop.ts similarity index 100% rename from src/hints/shouldSkipSquashLoop.ts rename to src/hints/dict/shouldSkipSquashLoop.ts diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index ecfc6e8b..397086f5 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -1,17 +1,17 @@ import { z } from 'zod'; import { allocSegmentParser } from './allocSegment'; -import { testLessThanParser } from './testLessThan'; -import { allocFelt252DictParser } from './allocFelt252Dict'; -import { getSegmentArenaIndexParser } from './getSegmentArenaIndex'; -import { felt252DictEntryInitParser } from './felt252DictEntryInit'; -import { felt252DictEntryUpdateParser } from './felt252DictEntryUpdate'; -import { initSquashDataParser } from './initSquashData'; -import { getCurrentAccessIndexParser } from './getCurrentAccessIndex'; -import { shouldSkipSquashLoopParser } from './shouldSkipSquashLoop'; -import { getCurrentAccessDeltaParser } from './getCurrentAccessDelta'; -import { shouldContinueSquashLoopParser } from './shouldContinueSquashLoop'; -import { getNextDictKeyParser } from './getNextDictKey'; +import { testLessThanParser } from './math/testLessThan'; +import { allocFelt252DictParser } from './dict/allocFelt252Dict'; +import { getSegmentArenaIndexParser } from './dict/getSegmentArenaIndex'; +import { felt252DictEntryInitParser } from './dict/felt252DictEntryInit'; +import { felt252DictEntryUpdateParser } from './dict/felt252DictEntryUpdate'; +import { initSquashDataParser } from './dict/initSquashData'; +import { getCurrentAccessIndexParser } from './dict/getCurrentAccessIndex'; +import { shouldSkipSquashLoopParser } from './dict/shouldSkipSquashLoop'; +import { getCurrentAccessDeltaParser } from './dict/getCurrentAccessDelta'; +import { shouldContinueSquashLoopParser } from './dict/shouldContinueSquashLoop'; +import { getNextDictKeyParser } from './dict/getNextDictKey'; import { assertLeIsFirstArcExcludedParser } from './assertLeIsFirstArcExcluded'; import { assertLeFindSmallArcsParser } from './assertLeFindSmallArc'; import { assertLeIsSecondArcExcludedParser } from './assertLeIsSecondArcExcluded'; diff --git a/src/hints/testLessThan.test.ts b/src/hints/math/testLessThan.test.ts similarity index 100% rename from src/hints/testLessThan.test.ts rename to src/hints/math/testLessThan.test.ts diff --git a/src/hints/testLessThan.ts b/src/hints/math/testLessThan.ts similarity index 100% rename from src/hints/testLessThan.ts rename to src/hints/math/testLessThan.ts diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 52ea784b..2a72921b 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -30,7 +30,7 @@ import { ResOp, } from 'hints/hintParamsSchema'; import { allocSegment, AllocSegment } from 'hints/allocSegment'; -import { testLessThan, TestLessThan } from 'hints/testLessThan'; +import { testLessThan, TestLessThan } from 'hints/math/testLessThan'; import { Hint } from 'hints/hintSchema'; import { HintName } from 'hints/hintName'; import { @@ -45,37 +45,40 @@ import { } from './instruction'; import { ScopeManager } from 'hints/scopeManager'; import { SquashedDictManager } from './squashedDict'; -import { allocFelt252Dict, AllocFelt252Dict } from 'hints/allocFelt252Dict'; +import { + allocFelt252Dict, + AllocFelt252Dict, +} from 'hints/dict/allocFelt252Dict'; import { getSegmentArenaIndex, GetSegmentArenaIndex, -} from 'hints/getSegmentArenaIndex'; +} from 'hints/dict/getSegmentArenaIndex'; import { felt252DictEntryInit, Felt252DictEntryInit, -} from 'hints/felt252DictEntryInit'; +} from 'hints/dict/felt252DictEntryInit'; import { Felt252DictEntryUpdate, felt252DictEntryUpdate, -} from 'hints/felt252DictEntryUpdate'; -import { initSquashData, InitSquashData } from 'hints/initSquashData'; +} from 'hints/dict/felt252DictEntryUpdate'; +import { initSquashData, InitSquashData } from 'hints/dict/initSquashData'; import { getCurrentAccessIndex, GetCurrentAccessIndex, -} from 'hints/getCurrentAccessIndex'; +} from 'hints/dict/getCurrentAccessIndex'; import { shouldSkipSquashLoop, ShouldSkipSquashLoop, -} from 'hints/shouldSkipSquashLoop'; +} from 'hints/dict/shouldSkipSquashLoop'; import { GetCurrentAccessDelta, getCurrentAccessDelta, -} from 'hints/getCurrentAccessDelta'; +} from 'hints/dict/getCurrentAccessDelta'; import { shouldContinueSquashLoop, ShouldContinueSquashLoop, -} from 'hints/shouldContinueSquashLoop'; -import { getNextDictKey, GetNextDictKey } from 'hints/getNextDictKey'; +} from 'hints/dict/shouldContinueSquashLoop'; +import { getNextDictKey, GetNextDictKey } from 'hints/dict/getNextDictKey'; import { assertLeFindSmallArcs, AssertLeFindSmallArcs, From 266ceef371e20b46ce13122c4653d3b7d82fd372 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 14:33:32 +0200 Subject: [PATCH 32/78] refactor: export hint handlers to its own file --- src/hints/hintHandler.ts | 121 ++++++++++++++++++++++++++++++++++++++ src/vm/virtualMachine.ts | 122 +-------------------------------------- 2 files changed, 123 insertions(+), 120 deletions(-) create mode 100644 src/hints/hintHandler.ts diff --git a/src/hints/hintHandler.ts b/src/hints/hintHandler.ts new file mode 100644 index 00000000..071846c2 --- /dev/null +++ b/src/hints/hintHandler.ts @@ -0,0 +1,121 @@ +import { VirtualMachine } from 'vm/virtualMachine'; +import { AllocSegment, allocSegment } from './allocSegment'; +import { + AssertLeFindSmallArcs, + assertLeFindSmallArcs, +} from './assertLeFindSmallArc'; +import { + AssertLeIsFirstArcExcluded, + assertLeIsFirstArcExcluded, +} from './assertLeIsFirstArcExcluded'; +import { + AssertLeIsSecondArcExcluded, + assertLeIsSecondArcExcluded, +} from './assertLeIsSecondArcExcluded'; +import { AllocFelt252Dict, allocFelt252Dict } from './dict/allocFelt252Dict'; +import { + Felt252DictEntryInit, + felt252DictEntryInit, +} from './dict/felt252DictEntryInit'; +import { + Felt252DictEntryUpdate, + felt252DictEntryUpdate, +} from './dict/felt252DictEntryUpdate'; +import { + GetCurrentAccessDelta, + getCurrentAccessDelta, +} from './dict/getCurrentAccessDelta'; +import { + GetCurrentAccessIndex, + getCurrentAccessIndex, +} from './dict/getCurrentAccessIndex'; +import { GetNextDictKey, getNextDictKey } from './dict/getNextDictKey'; +import { + GetSegmentArenaIndex, + getSegmentArenaIndex, +} from './dict/getSegmentArenaIndex'; +import { InitSquashData, initSquashData } from './dict/initSquashData'; +import { + ShouldContinueSquashLoop, + shouldContinueSquashLoop, +} from './dict/shouldContinueSquashLoop'; +import { + ShouldSkipSquashLoop, + shouldSkipSquashLoop, +} from './dict/shouldSkipSquashLoop'; +import { HintName } from './hintName'; +import { Hint } from './hintSchema'; +import { TestLessThan, testLessThan } from './math/testLessThan'; + +export const handlers: Record< + HintName, + (vm: VirtualMachine, hint: Hint) => void +> = { + [HintName.AllocFelt252Dict]: (vm, hint) => { + const h = hint as AllocFelt252Dict; + allocFelt252Dict(vm, h.segmentArenaPtr); + }, + [HintName.AllocSegment]: (vm, hint) => { + const h = hint as AllocSegment; + allocSegment(vm, h.dst); + }, + [HintName.AssertLeFindSmallArcs]: (vm, hint) => { + const h = hint as AssertLeFindSmallArcs; + assertLeFindSmallArcs(vm, h.rangeCheckPtr, h.a, h.b); + }, + [HintName.AssertLeIsFirstArcExcluded]: (vm, hint) => { + const h = hint as AssertLeIsFirstArcExcluded; + assertLeIsFirstArcExcluded(vm, h.skipExcludeFirstArc); + }, + [HintName.AssertLeIsSecondArcExcluded]: (vm, hint) => { + const h = hint as AssertLeIsSecondArcExcluded; + assertLeIsSecondArcExcluded(vm, h.skipExcludeSecondArc); + }, + [HintName.Felt252DictEntryInit]: (vm, hint) => { + const h = hint as Felt252DictEntryInit; + felt252DictEntryInit(vm, h.dictPtr, h.key); + }, + [HintName.Felt252DictEntryUpdate]: (vm, hint) => { + const h = hint as Felt252DictEntryUpdate; + felt252DictEntryUpdate(vm, h.dictPtr, h.value); + }, + [HintName.GetCurrentAccessDelta]: (vm, hint) => { + const h = hint as GetCurrentAccessDelta; + getCurrentAccessDelta(vm, h.indexDeltaMinusOne); + }, + [HintName.GetCurrentAccessIndex]: (vm, hint) => { + const h = hint as GetCurrentAccessIndex; + getCurrentAccessIndex(vm, h.rangeCheckPtr); + }, + [HintName.GetNextDictKey]: (vm, hint) => { + const h = hint as GetNextDictKey; + getNextDictKey(vm, h.nextKey); + }, + [HintName.GetSegmentArenaIndex]: (vm, hint) => { + const h = hint as GetSegmentArenaIndex; + getSegmentArenaIndex(vm, h.dict_end_ptr, h.dict_index); + }, + [HintName.InitSquashData]: (vm, hint) => { + const h = hint as InitSquashData; + initSquashData( + vm, + h.dictAccesses, + h.ptrDiff, + h.nAccesses, + h.bigKeys, + h.firstKey + ); + }, + [HintName.ShouldContinueSquashLoop]: (vm, hint) => { + const h = hint as ShouldContinueSquashLoop; + shouldContinueSquashLoop(vm, h.shouldContinue); + }, + [HintName.ShouldSkipSquashLoop]: (vm, hint) => { + const h = hint as ShouldSkipSquashLoop; + shouldSkipSquashLoop(vm, h.shouldSkipLoop); + }, + [HintName.TestLessThan]: (vm, hint) => { + const h = hint as TestLessThan; + testLessThan(vm, h.lhs, h.rhs, h.dst); + }, +}; diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 2a72921b..5427fc8d 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -29,10 +29,7 @@ import { OpType, ResOp, } from 'hints/hintParamsSchema'; -import { allocSegment, AllocSegment } from 'hints/allocSegment'; -import { testLessThan, TestLessThan } from 'hints/math/testLessThan'; import { Hint } from 'hints/hintSchema'; -import { HintName } from 'hints/hintName'; import { ApUpdate, FpUpdate, @@ -45,52 +42,7 @@ import { } from './instruction'; import { ScopeManager } from 'hints/scopeManager'; import { SquashedDictManager } from './squashedDict'; -import { - allocFelt252Dict, - AllocFelt252Dict, -} from 'hints/dict/allocFelt252Dict'; -import { - getSegmentArenaIndex, - GetSegmentArenaIndex, -} from 'hints/dict/getSegmentArenaIndex'; -import { - felt252DictEntryInit, - Felt252DictEntryInit, -} from 'hints/dict/felt252DictEntryInit'; -import { - Felt252DictEntryUpdate, - felt252DictEntryUpdate, -} from 'hints/dict/felt252DictEntryUpdate'; -import { initSquashData, InitSquashData } from 'hints/dict/initSquashData'; -import { - getCurrentAccessIndex, - GetCurrentAccessIndex, -} from 'hints/dict/getCurrentAccessIndex'; -import { - shouldSkipSquashLoop, - ShouldSkipSquashLoop, -} from 'hints/dict/shouldSkipSquashLoop'; -import { - GetCurrentAccessDelta, - getCurrentAccessDelta, -} from 'hints/dict/getCurrentAccessDelta'; -import { - shouldContinueSquashLoop, - ShouldContinueSquashLoop, -} from 'hints/dict/shouldContinueSquashLoop'; -import { getNextDictKey, GetNextDictKey } from 'hints/dict/getNextDictKey'; -import { - assertLeFindSmallArcs, - AssertLeFindSmallArcs, -} from 'hints/assertLeFindSmallArc'; -import { - assertLeIsFirstArcExcluded, - AssertLeIsFirstArcExcluded, -} from 'hints/assertLeIsFirstArcExcluded'; -import { - assertLeIsSecondArcExcluded, - AssertLeIsSecondArcExcluded, -} from 'hints/assertLeIsSecondArcExcluded'; +import { handlers } from 'hints/hintHandler'; export type TraceEntry = { pc: Relocatable; @@ -130,77 +82,7 @@ export class VirtualMachine { relocatedTrace: RelocatedTraceEntry[]; /** Maps a hint to its implementation */ - private handlers: Record void> = - { - [HintName.AllocSegment]: (vm, hint) => { - const h = hint as AllocSegment; - allocSegment(vm, h.dst); - }, - [HintName.TestLessThan]: (vm, hint) => { - const h = hint as TestLessThan; - testLessThan(vm, h.lhs, h.rhs, h.dst); - }, - [HintName.AllocFelt252Dict]: (vm, hint) => { - const h = hint as AllocFelt252Dict; - allocFelt252Dict(vm, h.segmentArenaPtr); - }, - [HintName.GetSegmentArenaIndex]: (vm, hint) => { - const h = hint as GetSegmentArenaIndex; - getSegmentArenaIndex(vm, h.dict_end_ptr, h.dict_index); - }, - [HintName.Felt252DictEntryInit]: (vm, hint) => { - const h = hint as Felt252DictEntryInit; - felt252DictEntryInit(vm, h.dictPtr, h.key); - }, - [HintName.Felt252DictEntryUpdate]: (vm, hint) => { - const h = hint as Felt252DictEntryUpdate; - felt252DictEntryUpdate(vm, h.dictPtr, h.value); - }, - - [HintName.InitSquashData]: (vm, hint) => { - const h = hint as InitSquashData; - initSquashData( - vm, - h.dictAccesses, - h.ptrDiff, - h.nAccesses, - h.bigKeys, - h.firstKey - ); - }, - [HintName.GetCurrentAccessIndex]: (vm, hint) => { - const h = hint as GetCurrentAccessIndex; - getCurrentAccessIndex(vm, h.rangeCheckPtr); - }, - [HintName.ShouldSkipSquashLoop]: (vm, hint) => { - const h = hint as ShouldSkipSquashLoop; - shouldSkipSquashLoop(vm, h.shouldSkipLoop); - }, - [HintName.GetCurrentAccessDelta]: (vm, hint) => { - const h = hint as GetCurrentAccessDelta; - getCurrentAccessDelta(vm, h.indexDeltaMinusOne); - }, - [HintName.ShouldContinueSquashLoop]: (vm, hint) => { - const h = hint as ShouldContinueSquashLoop; - shouldContinueSquashLoop(vm, h.shouldContinue); - }, - [HintName.GetNextDictKey]: (vm, hint) => { - const h = hint as GetNextDictKey; - getNextDictKey(vm, h.nextKey); - }, - [HintName.AssertLeFindSmallArcs]: (vm, hint) => { - const h = hint as AssertLeFindSmallArcs; - assertLeFindSmallArcs(vm, h.rangeCheckPtr, h.a, h.b); - }, - [HintName.AssertLeIsFirstArcExcluded]: (vm, hint) => { - const h = hint as AssertLeIsFirstArcExcluded; - assertLeIsFirstArcExcluded(vm, h.skipExcludeFirstArc); - }, - [HintName.AssertLeIsSecondArcExcluded]: (vm, hint) => { - const h = hint as AssertLeIsSecondArcExcluded; - assertLeIsSecondArcExcluded(vm, h.skipExcludeSecondArc); - }, - }; + private handlers = handlers; constructor() { this.currentStep = 0n; From d5b1d4b273c5efd3844bd4d0c457793e6c784ae5 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 14:33:50 +0200 Subject: [PATCH 33/78] refactor: sort hints by ascending alphabetical order --- src/hints/hintName.ts | 18 +++++++++--------- src/hints/hintSchema.ts | 39 +++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index 6777fcf0..f00a024a 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -1,18 +1,18 @@ /** Name to identify which hint is executed */ export enum HintName { - AllocSegment = 'AllocSegment', - TestLessThan = 'TestLessThan', AllocFelt252Dict = 'AllocFelt252Dict', + AllocSegment = 'AllocSegment', + AssertLeFindSmallArcs = 'AssertLeFindSmallArcs', + AssertLeIsFirstArcExcluded = 'AssertLeIsFirstArcExcluded', + AssertLeIsSecondArcExcluded = 'AssertLeIsSecondArcExcluded', Felt252DictEntryInit = 'Felt252DictEntryInit', Felt252DictEntryUpdate = 'Felt252DictEntryUpdate', + GetCurrentAccessDelta = 'GetCurrentAccessDelta', + GetCurrentAccessIndex = 'GetCurrentAccessIndex', + GetNextDictKey = 'GetNextDictKey', GetSegmentArenaIndex = 'GetSegmentArenaIndex', InitSquashData = 'InitSquashData', - GetCurrentAccessIndex = 'GetCurrentAccessIndex', - ShouldSkipSquashLoop = 'ShouldSkipSquashLoop', - GetCurrentAccessDelta = 'GetCurrentAccessDelta', ShouldContinueSquashLoop = 'ShouldContinueSquashLoop', - GetNextDictKey = 'GetNextDictKey', - AssertLeFindSmallArcs = 'AssertLeFindSmallArcs', - AssertLeIsFirstArcExcluded = 'AssertLeIsFirstArcExcluded', - AssertLeIsSecondArcExcluded = 'AssertLeIsSecondArcExcluded', + ShouldSkipSquashLoop = 'ShouldSkipSquashLoop', + TestLessThan = 'TestLessThan', } diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 397086f5..c2212798 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -1,38 +1,37 @@ import { z } from 'zod'; - import { allocSegmentParser } from './allocSegment'; -import { testLessThanParser } from './math/testLessThan'; +import { assertLeFindSmallArcsParser } from './assertLeFindSmallArc'; +import { assertLeIsFirstArcExcludedParser } from './assertLeIsFirstArcExcluded'; +import { assertLeIsSecondArcExcludedParser } from './assertLeIsSecondArcExcluded'; import { allocFelt252DictParser } from './dict/allocFelt252Dict'; -import { getSegmentArenaIndexParser } from './dict/getSegmentArenaIndex'; import { felt252DictEntryInitParser } from './dict/felt252DictEntryInit'; import { felt252DictEntryUpdateParser } from './dict/felt252DictEntryUpdate'; -import { initSquashDataParser } from './dict/initSquashData'; -import { getCurrentAccessIndexParser } from './dict/getCurrentAccessIndex'; -import { shouldSkipSquashLoopParser } from './dict/shouldSkipSquashLoop'; import { getCurrentAccessDeltaParser } from './dict/getCurrentAccessDelta'; -import { shouldContinueSquashLoopParser } from './dict/shouldContinueSquashLoop'; +import { getCurrentAccessIndexParser } from './dict/getCurrentAccessIndex'; import { getNextDictKeyParser } from './dict/getNextDictKey'; -import { assertLeIsFirstArcExcludedParser } from './assertLeIsFirstArcExcluded'; -import { assertLeFindSmallArcsParser } from './assertLeFindSmallArc'; -import { assertLeIsSecondArcExcludedParser } from './assertLeIsSecondArcExcluded'; +import { getSegmentArenaIndexParser } from './dict/getSegmentArenaIndex'; +import { initSquashDataParser } from './dict/initSquashData'; +import { shouldContinueSquashLoopParser } from './dict/shouldContinueSquashLoop'; +import { shouldSkipSquashLoopParser } from './dict/shouldSkipSquashLoop'; +import { testLessThanParser } from './math/testLessThan'; /** Zod object to parse any implemented hints */ const hint = z.union([ - allocSegmentParser, - testLessThanParser, allocFelt252DictParser, - getSegmentArenaIndexParser, + allocSegmentParser, + assertLeFindSmallArcsParser, + assertLeIsFirstArcExcludedParser, + assertLeIsSecondArcExcludedParser, felt252DictEntryInitParser, felt252DictEntryUpdateParser, - initSquashDataParser, - getCurrentAccessIndexParser, - shouldSkipSquashLoopParser, getCurrentAccessDeltaParser, - shouldContinueSquashLoopParser, + getCurrentAccessIndexParser, getNextDictKeyParser, - assertLeFindSmallArcsParser, - assertLeIsFirstArcExcludedParser, - assertLeIsSecondArcExcludedParser, + getSegmentArenaIndexParser, + initSquashDataParser, + shouldContinueSquashLoopParser, + shouldSkipSquashLoopParser, + testLessThanParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ From 732723d4b0d63efc59a3e02e1418cc0fc69dc6fc Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 14:45:30 +0200 Subject: [PATCH 34/78] feat: add unit test ShouldContinueSquashLoop hint --- .../dict/shouldContinueSquashLoop.test.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/hints/dict/shouldContinueSquashLoop.test.ts diff --git a/src/hints/dict/shouldContinueSquashLoop.test.ts b/src/hints/dict/shouldContinueSquashLoop.test.ts new file mode 100644 index 00000000..deb8331c --- /dev/null +++ b/src/hints/dict/shouldContinueSquashLoop.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; +import { shouldContinueSquashLoopParser } from './shouldContinueSquashLoop'; + +const SHOULD_CONTINUE_SQUASH_LOOP = { + ShouldContinueSquashLoop: { + should_continue: { + register: 'AP', + offset: 0, + }, + }, +}; + +describe('ShouldContinueSquashLoop', () => { + test('should properly parse ShouldContinueSquashLoop hint', () => { + const hint = shouldContinueSquashLoopParser.parse( + SHOULD_CONTINUE_SQUASH_LOOP + ); + expect(hint).toEqual({ + type: HintName.ShouldContinueSquashLoop, + shouldContinue: { + register: Register.Ap, + offset: 0, + }, + }); + }); + + test.each([ + [[new Felt(4n)], new Felt(0n)], + [[new Felt(13n), new Felt(15n)], new Felt(1n)], + ])( + 'should properly execute ShouldContinueSquashLoop hint', + (values, flag) => { + const hint = shouldContinueSquashLoopParser.parse( + SHOULD_CONTINUE_SQUASH_LOOP + ); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + + vm.squashedDictManager.keys = [new Felt(0n), new Felt(1n)]; + vm.squashedDictManager.keyToIndices.set('1', values); + + vm.executeHint(hint); + + expect(vm.memory.get(vm.ap)).toEqual(flag); + } + ); +}); From 3c95ab0fe91356a0bece2ab4b393aa98bae5c24f Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 14:48:49 +0200 Subject: [PATCH 35/78] feat: add unit test ShouldSkipSquashLoop --- src/hints/dict/shouldSkipSquashLoop.test.ts | 46 +++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/hints/dict/shouldSkipSquashLoop.test.ts diff --git a/src/hints/dict/shouldSkipSquashLoop.test.ts b/src/hints/dict/shouldSkipSquashLoop.test.ts new file mode 100644 index 00000000..302ca695 --- /dev/null +++ b/src/hints/dict/shouldSkipSquashLoop.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; +import { shouldSkipSquashLoopParser } from './shouldSkipSquashLoop'; + +const SHOULD_SKIP_SQUASH_LOOP = { + ShouldSkipSquashLoop: { + should_skip_loop: { + register: 'AP', + offset: 0, + }, + }, +}; + +describe('shouldSkipSquashLoop', () => { + test('should properly parse shouldSkipSquashLoop hint', () => { + const hint = shouldSkipSquashLoopParser.parse(SHOULD_SKIP_SQUASH_LOOP); + expect(hint).toEqual({ + type: HintName.ShouldSkipSquashLoop, + shouldSkipLoop: { + register: Register.Ap, + offset: 0, + }, + }); + }); + + test.each([ + [[new Felt(4n)], new Felt(1n)], + [[new Felt(13n), new Felt(15n)], new Felt(0n)], + ])('should properly execute shouldSkipSquashLoop hint', (values, flag) => { + const hint = shouldSkipSquashLoopParser.parse(SHOULD_SKIP_SQUASH_LOOP); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + + vm.squashedDictManager.keys = [new Felt(0n), new Felt(1n)]; + vm.squashedDictManager.keyToIndices.set('1', values); + + vm.executeHint(hint); + + expect(vm.memory.get(vm.ap)).toEqual(flag); + }); +}); From 076d54fb3a3af8fd26821d14ef91a466e9b85dd7 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 15:05:45 +0200 Subject: [PATCH 36/78] feat: add unit test GetNextDictKey hint --- src/hints/dict/getNextDictKey.test.ts | 43 +++++++++++++++++++++ src/hints/dict/shouldSkipSquashLoop.test.ts | 4 +- 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/hints/dict/getNextDictKey.test.ts diff --git a/src/hints/dict/getNextDictKey.test.ts b/src/hints/dict/getNextDictKey.test.ts new file mode 100644 index 00000000..a5f21b93 --- /dev/null +++ b/src/hints/dict/getNextDictKey.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; +import { getNextDictKeyParser } from './getNextDictKey'; + +const GET_NEXT_DICT_KEY = { + GetNextDictKey: { + next_key: { + register: 'FP', + offset: 0, + }, + }, +}; + +describe('GetNextDictKey', () => { + test('should properly parse GetNextDictKey hint', () => { + const hint = getNextDictKeyParser.parse(GET_NEXT_DICT_KEY); + expect(hint).toEqual({ + type: HintName.GetNextDictKey, + nextKey: { + register: Register.Fp, + offset: 0, + }, + }); + }); + + test('should properly execute GetNextDictKey hint', () => { + const hint = getNextDictKeyParser.parse(GET_NEXT_DICT_KEY); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + + const key = new Felt(0n); + vm.squashedDictManager.keys = [key, new Felt(1n)]; + + vm.executeHint(hint); + + expect(vm.memory.get(vm.fp)).toEqual(key); + }); +}); diff --git a/src/hints/dict/shouldSkipSquashLoop.test.ts b/src/hints/dict/shouldSkipSquashLoop.test.ts index 302ca695..70105a17 100644 --- a/src/hints/dict/shouldSkipSquashLoop.test.ts +++ b/src/hints/dict/shouldSkipSquashLoop.test.ts @@ -16,7 +16,7 @@ const SHOULD_SKIP_SQUASH_LOOP = { }; describe('shouldSkipSquashLoop', () => { - test('should properly parse shouldSkipSquashLoop hint', () => { + test('should properly parse ShouldSkipSquashLoop hint', () => { const hint = shouldSkipSquashLoopParser.parse(SHOULD_SKIP_SQUASH_LOOP); expect(hint).toEqual({ type: HintName.ShouldSkipSquashLoop, @@ -30,7 +30,7 @@ describe('shouldSkipSquashLoop', () => { test.each([ [[new Felt(4n)], new Felt(1n)], [[new Felt(13n), new Felt(15n)], new Felt(0n)], - ])('should properly execute shouldSkipSquashLoop hint', (values, flag) => { + ])('should properly execute ShouldSkipSquashLoop hint', (values, flag) => { const hint = shouldSkipSquashLoopParser.parse(SHOULD_SKIP_SQUASH_LOOP); const vm = new VirtualMachine(); vm.memory.addSegment(); From 04670e7deeccbdc6f46a27734fc26ec279e0e543 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 15:14:44 +0200 Subject: [PATCH 37/78] feat: add unit test GetCurrentAccessIndex --- src/hints/dict/getCurrentAccessIndex.test.ts | 55 ++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/hints/dict/getCurrentAccessIndex.test.ts diff --git a/src/hints/dict/getCurrentAccessIndex.test.ts b/src/hints/dict/getCurrentAccessIndex.test.ts new file mode 100644 index 00000000..11451bc3 --- /dev/null +++ b/src/hints/dict/getCurrentAccessIndex.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; +import { getCurrentAccessIndexParser } from './getCurrentAccessIndex'; +import { OpType } from 'hints/hintParamsSchema'; +import { rangeCheckHandler } from 'builtins/rangeCheck'; + +const GET_CURRENT_ACCESS_INDEX = { + GetCurrentAccessIndex: { + range_check_ptr: { + Deref: { + register: 'AP', + offset: 0, + }, + }, + }, +}; + +describe('GetCurrentAccessIndex', () => { + test('should properly parse GetCurrentAccessIndex hint', () => { + const hint = getCurrentAccessIndexParser.parse(GET_CURRENT_ACCESS_INDEX); + expect(hint).toEqual({ + type: HintName.GetCurrentAccessIndex, + rangeCheckPtr: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 0, + }, + }, + }); + }); + + test('should properly execute GetCurrentAccessIndex hint', () => { + const hint = getCurrentAccessIndexParser.parse(GET_CURRENT_ACCESS_INDEX); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const rangeCheck = vm.memory.addSegment(rangeCheckHandler(128n)); + + vm.memory.assertEq(vm.ap, rangeCheck); + + const key = new Felt(0n); + const indices = [new Felt(6n), new Felt(3n), new Felt(35n)]; + vm.squashedDictManager.keyToIndices.set(key.toString(), indices); + vm.squashedDictManager.keys = [key]; + + vm.executeHint(hint); + + expect(vm.memory.get(rangeCheck)).toEqual(indices[indices.length - 1]); + }); +}); From 0a5ad6f8655e72b9b946abcc377692afa9ed8988 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 15:23:26 +0200 Subject: [PATCH 38/78] feat: add unit test GetCurrentAccessDelta hint --- src/hints/dict/getCurrentAccessDelta.test.ts | 49 ++++++++++++++++++++ src/hints/dict/getCurrentAccessIndex.test.ts | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/hints/dict/getCurrentAccessDelta.test.ts diff --git a/src/hints/dict/getCurrentAccessDelta.test.ts b/src/hints/dict/getCurrentAccessDelta.test.ts new file mode 100644 index 00000000..9aa06070 --- /dev/null +++ b/src/hints/dict/getCurrentAccessDelta.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; +import { getCurrentAccessDeltaParser } from './getCurrentAccessDelta'; + +const GET_CURRENT_ACCESS_INDEX = { + GetCurrentAccessDelta: { + index_delta_minus1: { + register: 'AP', + offset: 0, + }, + }, +}; + +describe('GetCurrentAccessDelta', () => { + test('should properly parse GetCurrentAccessDelta hint', () => { + const hint = getCurrentAccessDeltaParser.parse(GET_CURRENT_ACCESS_INDEX); + expect(hint).toEqual({ + type: HintName.GetCurrentAccessDelta, + indexDeltaMinusOne: { + register: Register.Ap, + offset: 0, + }, + }); + }); + + test('should properly execute GetCurrentAccessDelta hint', () => { + const hint = getCurrentAccessDeltaParser.parse(GET_CURRENT_ACCESS_INDEX); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + + const key = new Felt(0n); + const poppedIndex = new Felt(3n); + const lastIndex = new Felt(35n); + const indices = [new Felt(40n), lastIndex, poppedIndex]; + vm.squashedDictManager.keyToIndices.set(key.toString(), indices); + vm.squashedDictManager.keys = [key]; + + vm.executeHint(hint); + + expect(vm.memory.get(vm.ap)).toEqual( + lastIndex.sub(poppedIndex).sub(new Felt(1n)) + ); + }); +}); diff --git a/src/hints/dict/getCurrentAccessIndex.test.ts b/src/hints/dict/getCurrentAccessIndex.test.ts index 11451bc3..7d5e8e2b 100644 --- a/src/hints/dict/getCurrentAccessIndex.test.ts +++ b/src/hints/dict/getCurrentAccessIndex.test.ts @@ -44,7 +44,7 @@ describe('GetCurrentAccessIndex', () => { vm.memory.assertEq(vm.ap, rangeCheck); const key = new Felt(0n); - const indices = [new Felt(6n), new Felt(3n), new Felt(35n)]; + const indices = [new Felt(35n), new Felt(6n), new Felt(3n)]; vm.squashedDictManager.keyToIndices.set(key.toString(), indices); vm.squashedDictManager.keys = [key]; From 34f9d7aa86f2b7e194b8ad4e17020133c813f097 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 16:38:15 +0200 Subject: [PATCH 39/78] feat: add unit test InitSquashData hint --- src/hints/dict/initSquashData.test.ts | 162 ++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 src/hints/dict/initSquashData.test.ts diff --git a/src/hints/dict/initSquashData.test.ts b/src/hints/dict/initSquashData.test.ts new file mode 100644 index 00000000..47476f58 --- /dev/null +++ b/src/hints/dict/initSquashData.test.ts @@ -0,0 +1,162 @@ +import { describe, expect, test } from 'bun:test'; + +import { Register } from 'vm/instruction'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { HintName } from 'hints/hintName'; +import { initSquashDataParser } from './initSquashData'; +import { OpType } from 'hints/hintParamsSchema'; +import { Felt } from 'primitives/felt'; +import { segmentArenaHandler } from 'builtins/segmentArena'; +import { allocFelt252DictParser } from './allocFelt252Dict'; +import { Relocatable } from 'primitives/relocatable'; +import { SquashedDictManager } from 'vm/squashedDict'; + +const initSegmentArenaBuiltin = (vm: VirtualMachine) => { + const info = [ + vm.memory.addSegment(segmentArenaHandler), + new Felt(0n), + new Felt(0n), + ]; + const base = vm.memory.addSegment(segmentArenaHandler); + info.map((value, offset) => vm.memory.assertEq(base.add(offset), value)); + return base.add(info.length); +}; + +const ALLOC_FELT252_DICT = { + AllocFelt252Dict: { + segment_arena_ptr: { + Deref: { + register: 'AP', + offset: 0, + }, + }, + }, +}; + +const INIT_SQUASH_DATA = { + InitSquashData: { + dict_accesses: { + Deref: { + register: 'AP', + offset: 1, + }, + }, + ptr_diff: { + Deref: { + register: 'AP', + offset: 2, + }, + }, + n_accesses: { + Deref: { + register: 'AP', + offset: 3, + }, + }, + big_keys: { + register: 'AP', + offset: 4, + }, + first_key: { + register: 'AP', + offset: 5, + }, + }, +}; + +describe('InitSquashData', () => { + test('should properly parse InitSquashData hint', () => { + const hint = initSquashDataParser.parse(INIT_SQUASH_DATA); + expect(hint).toEqual({ + type: HintName.InitSquashData, + dictAccesses: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 1, + }, + }, + ptrDiff: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 2, + }, + }, + nAccesses: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 3, + }, + }, + bigKeys: { + register: Register.Ap, + offset: 4, + }, + firstKey: { + register: Register.Ap, + offset: 5, + }, + }); + }); + + test('should properly execute InitSquashData hint', () => { + const allocHint = allocFelt252DictParser.parse(ALLOC_FELT252_DICT); + const hint = initSquashDataParser.parse(INIT_SQUASH_DATA); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const arenaPtr = initSegmentArenaBuiltin(vm); + + vm.memory.assertEq(vm.ap, arenaPtr); + vm.executeHint(allocHint); + + const dictPtr = new Relocatable(4, 0); + const key = new Felt(3n); + const values = [ + new Felt(0n), + new Felt(5n), + new Felt(15n), + new Felt(18n), + new Felt(66n), + ]; + + const dict = vm.dictManager.get(dictPtr.segmentId); + const dictAccessSize = 3; + expect(dict).not.toBeUndefined(); + if (!dict) throw new Error('Undefined dict'); + dict.set(key.toString(), values[values.length - 1]); + + values.reduce((prev, curr, i) => { + const index = i - 1; + const keyIndex = new Felt(BigInt(index * dictAccessSize)); + const prevIndex = new Felt(BigInt(index * dictAccessSize + 1)); + const currIndex = new Felt(BigInt(index * dictAccessSize + 2)); + const prevValue = prev !== undefined ? prev : new Felt(0n); + vm.memory.assertEq(dictPtr.add(keyIndex), key); + vm.memory.assertEq(dictPtr.add(prevIndex), prevValue); + vm.memory.assertEq(dictPtr.add(currIndex), curr); + return curr; + }); + + const nAccesses = values.length - 1; + const ptrDiff = nAccesses * dictAccessSize; + vm.memory.assertEq(vm.ap.add(new Felt(1n)), dictPtr); + vm.memory.assertEq(vm.ap.add(new Felt(2n)), new Felt(BigInt(ptrDiff))); + vm.memory.assertEq(vm.ap.add(new Felt(3n)), new Felt(BigInt(nAccesses))); + + vm.executeHint(hint); + + const expectedSquashedDict = new SquashedDictManager(); + expectedSquashedDict.keyToIndices.set(key.toString(), [ + new Felt(3n), + new Felt(2n), + new Felt(1n), + new Felt(0n), + ]); + expectedSquashedDict.keys = [key]; + + expect(vm.squashedDictManager).toEqual(expectedSquashedDict); + }); +}); From 594d06f8f8e56ecbb9965fa8fc2d5e80ca2c3fe2 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 16:44:33 +0200 Subject: [PATCH 40/78] refactor: rename filename squashedDict to squashedDictManager and move to hints folder --- src/hints/dict/initSquashData.test.ts | 2 +- .../squashedDictManager.test.ts} | 4 ++-- src/{vm/squashedDict.ts => hints/squashedDictManager.ts} | 0 src/vm/virtualMachine.ts | 7 ++++--- 4 files changed, 7 insertions(+), 6 deletions(-) rename src/{vm/squashedDict.test.ts => hints/squashedDictManager.test.ts} (95%) rename src/{vm/squashedDict.ts => hints/squashedDictManager.ts} (100%) diff --git a/src/hints/dict/initSquashData.test.ts b/src/hints/dict/initSquashData.test.ts index 47476f58..cf9465be 100644 --- a/src/hints/dict/initSquashData.test.ts +++ b/src/hints/dict/initSquashData.test.ts @@ -9,7 +9,7 @@ import { Felt } from 'primitives/felt'; import { segmentArenaHandler } from 'builtins/segmentArena'; import { allocFelt252DictParser } from './allocFelt252Dict'; import { Relocatable } from 'primitives/relocatable'; -import { SquashedDictManager } from 'vm/squashedDict'; +import { SquashedDictManager } from 'hints/squashedDictManager'; const initSegmentArenaBuiltin = (vm: VirtualMachine) => { const info = [ diff --git a/src/vm/squashedDict.test.ts b/src/hints/squashedDictManager.test.ts similarity index 95% rename from src/vm/squashedDict.test.ts rename to src/hints/squashedDictManager.test.ts index 9b706d7e..66442d24 100644 --- a/src/vm/squashedDict.test.ts +++ b/src/hints/squashedDictManager.test.ts @@ -1,9 +1,9 @@ import { describe, expect, test } from 'bun:test'; -import { SquashedDictManager } from './squashedDict'; +import { SquashedDictManager } from './squashedDictManager'; import { Felt } from 'primitives/felt'; import { EmptyKeys } from 'errors/squashedDict'; -describe('squashed dictionnary', () => { +describe('SquashedDictManager', () => { test('should properly initialize a SquashedDictManager', () => { const squashedDictManager = new SquashedDictManager(); expect(squashedDictManager.keys.length).toEqual(0); diff --git a/src/vm/squashedDict.ts b/src/hints/squashedDictManager.ts similarity index 100% rename from src/vm/squashedDict.ts rename to src/hints/squashedDictManager.ts diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 5427fc8d..a0bfd21f 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -30,6 +30,10 @@ import { ResOp, } from 'hints/hintParamsSchema'; import { Hint } from 'hints/hintSchema'; +import { ScopeManager } from 'hints/scopeManager'; +import { SquashedDictManager } from '../hints/squashedDictManager'; +import { handlers } from 'hints/hintHandler'; + import { ApUpdate, FpUpdate, @@ -40,9 +44,6 @@ import { Register, ResLogic, } from './instruction'; -import { ScopeManager } from 'hints/scopeManager'; -import { SquashedDictManager } from './squashedDict'; -import { handlers } from 'hints/hintHandler'; export type TraceEntry = { pc: Relocatable; From b19c6b7d9411bd5927a231ff4dccbfc6641f0376 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 17:00:59 +0200 Subject: [PATCH 41/78] chore: order imports --- src/hints/allocSegment.test.ts | 1 + src/hints/allocSegment.ts | 3 ++- src/hints/assertLeFindSmallArc.ts | 5 +++-- src/hints/assertLeIsFirstArcExcluded.ts | 5 +++-- src/hints/assertLeIsSecondArcExcluded.ts | 5 +++-- src/hints/dict/allocFelt252Dict.test.ts | 9 +++++---- src/hints/dict/allocFelt252Dict.ts | 10 ++++++---- src/hints/dict/felt252DictEntryInit.test.ts | 9 +++++---- src/hints/dict/felt252DictEntryInit.ts | 3 ++- src/hints/dict/felt252DictEntryUpdate.test.ts | 11 ++++++----- src/hints/dict/felt252DictEntryUpdate.ts | 8 +++++--- src/hints/dict/getCurrentAccessDelta.test.ts | 3 ++- src/hints/dict/getCurrentAccessDelta.ts | 5 +++-- src/hints/dict/getCurrentAccessIndex.test.ts | 7 ++++--- src/hints/dict/getCurrentAccessIndex.ts | 1 + src/hints/dict/getNextDictKey.test.ts | 3 ++- src/hints/dict/getNextDictKey.ts | 3 ++- src/hints/dict/getSegmentArenaIndex.test.ts | 7 ++++--- src/hints/dict/getSegmentArenaIndex.ts | 3 ++- src/hints/dict/initSquashData.test.ts | 11 ++++++----- src/hints/dict/initSquashData.ts | 10 ++++++---- src/hints/dict/shouldContinueSquashLoop.test.ts | 3 ++- src/hints/dict/shouldContinueSquashLoop.ts | 3 ++- src/hints/dict/shouldSkipSquashLoop.test.ts | 3 ++- src/hints/dict/shouldSkipSquashLoop.ts | 3 ++- src/hints/hintHandler.ts | 6 ++++-- src/hints/hintParamsSchema.test.ts | 1 + src/hints/hintSchema.ts | 1 + src/hints/math/testLessThan.test.ts | 1 + src/hints/math/testLessThan.ts | 3 ++- src/hints/scopeManager.test.ts | 4 +++- src/hints/squashedDictManager.test.ts | 6 ++++-- src/hints/squashedDictManager.ts | 1 + 33 files changed, 98 insertions(+), 59 deletions(-) diff --git a/src/hints/allocSegment.test.ts b/src/hints/allocSegment.test.ts index 71ef19f0..730fdfa4 100644 --- a/src/hints/allocSegment.test.ts +++ b/src/hints/allocSegment.test.ts @@ -3,6 +3,7 @@ import { describe, expect, test } from 'bun:test'; import { Relocatable } from 'primitives/relocatable'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; + import { HintName } from 'hints/hintName'; import { allocSegmentParser } from './allocSegment'; diff --git a/src/hints/allocSegment.ts b/src/hints/allocSegment.ts index d6be40c0..572df684 100644 --- a/src/hints/allocSegment.ts +++ b/src/hints/allocSegment.ts @@ -1,8 +1,9 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; -import { cellRef, CellRef } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; /** Zod object to parse AllocSegment hint */ export const allocSegmentParser = z diff --git a/src/hints/assertLeFindSmallArc.ts b/src/hints/assertLeFindSmallArc.ts index 3dd7db30..4cebc066 100644 --- a/src/hints/assertLeFindSmallArc.ts +++ b/src/hints/assertLeFindSmallArc.ts @@ -1,9 +1,10 @@ import { z } from 'zod'; +import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; -import { ResOp, resOp } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; +import { ResOp, resOp } from 'hints/hintParamsSchema'; /** Zod object to parse AssertLeFindSmallArcs hint */ export const assertLeFindSmallArcsParser = z diff --git a/src/hints/assertLeIsFirstArcExcluded.ts b/src/hints/assertLeIsFirstArcExcluded.ts index 746f46c1..fe0f9bf4 100644 --- a/src/hints/assertLeIsFirstArcExcluded.ts +++ b/src/hints/assertLeIsFirstArcExcluded.ts @@ -1,9 +1,10 @@ import { z } from 'zod'; +import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; -import { cellRef, CellRef } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; import { Arc } from './assertLeFindSmallArc'; /** Zod object to parse AssertLeIsFirstArcExcluded hint */ diff --git a/src/hints/assertLeIsSecondArcExcluded.ts b/src/hints/assertLeIsSecondArcExcluded.ts index 884d2a0c..bb37ae56 100644 --- a/src/hints/assertLeIsSecondArcExcluded.ts +++ b/src/hints/assertLeIsSecondArcExcluded.ts @@ -1,9 +1,10 @@ import { z } from 'zod'; +import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; -import { cellRef, CellRef } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; import { Arc } from './assertLeFindSmallArc'; /** Zod object to parse AssertLeIsSecondArcExcluded hint */ diff --git a/src/hints/dict/allocFelt252Dict.test.ts b/src/hints/dict/allocFelt252Dict.test.ts index 57de7235..e435eac2 100644 --- a/src/hints/dict/allocFelt252Dict.test.ts +++ b/src/hints/dict/allocFelt252Dict.test.ts @@ -1,13 +1,14 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; +import { Relocatable } from 'primitives/relocatable'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; +import { segmentArenaHandler } from 'builtins/segmentArena'; + import { HintName } from 'hints/hintName'; +import { OpType } from 'hints/hintParamsSchema'; import { allocFelt252DictParser } from './allocFelt252Dict'; -import { OpType } from '../hintParamsSchema'; -import { Felt } from 'primitives/felt'; -import { segmentArenaHandler } from 'builtins/segmentArena'; -import { Relocatable } from 'primitives/relocatable'; const initSegmentArenaBuiltin = (vm: VirtualMachine) => { const info = [ diff --git a/src/hints/dict/allocFelt252Dict.ts b/src/hints/dict/allocFelt252Dict.ts index 3eaa4ed9..c85c75d7 100644 --- a/src/hints/dict/allocFelt252Dict.ts +++ b/src/hints/dict/allocFelt252Dict.ts @@ -1,11 +1,13 @@ import { z } from 'zod'; -import { VirtualMachine } from 'vm/virtualMachine'; -import { resOp, ResOp } from 'hints/hintParamsSchema'; -import { HintName } from 'hints/hintName'; -import { isFelt, isRelocatable } from 'primitives/segmentValue'; import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; + import { Felt } from 'primitives/felt'; +import { isFelt, isRelocatable } from 'primitives/segmentValue'; +import { VirtualMachine } from 'vm/virtualMachine'; + +import { HintName } from 'hints/hintName'; +import { resOp, ResOp } from 'hints/hintParamsSchema'; /** Zod object to parse AllocFelt252Dict hint */ export const allocFelt252DictParser = z diff --git a/src/hints/dict/felt252DictEntryInit.test.ts b/src/hints/dict/felt252DictEntryInit.test.ts index 6eba6dee..d920b1fe 100644 --- a/src/hints/dict/felt252DictEntryInit.test.ts +++ b/src/hints/dict/felt252DictEntryInit.test.ts @@ -1,12 +1,13 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; +import { Relocatable } from 'primitives/relocatable'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; -import { HintName } from 'hints/hintName'; -import { OpType } from '../hintParamsSchema'; -import { Felt } from 'primitives/felt'; import { segmentArenaHandler } from 'builtins/segmentArena'; -import { Relocatable } from 'primitives/relocatable'; + +import { HintName } from 'hints/hintName'; +import { OpType } from 'hints/hintParamsSchema'; import { allocFelt252DictParser } from './allocFelt252Dict'; import { felt252DictEntryInitParser } from './felt252DictEntryInit'; diff --git a/src/hints/dict/felt252DictEntryInit.ts b/src/hints/dict/felt252DictEntryInit.ts index 80c7c3e9..88cf1abd 100644 --- a/src/hints/dict/felt252DictEntryInit.ts +++ b/src/hints/dict/felt252DictEntryInit.ts @@ -1,9 +1,10 @@ import { z } from 'zod'; +import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; + import { resOp, ResOp } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; /** Zod object to parse Felt252DictEntryInit hint */ export const felt252DictEntryInitParser = z diff --git a/src/hints/dict/felt252DictEntryUpdate.test.ts b/src/hints/dict/felt252DictEntryUpdate.test.ts index 710a5b19..29fd5cbc 100644 --- a/src/hints/dict/felt252DictEntryUpdate.test.ts +++ b/src/hints/dict/felt252DictEntryUpdate.test.ts @@ -1,15 +1,16 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; +import { Relocatable } from 'primitives/relocatable'; +import { SegmentValue } from 'primitives/segmentValue'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; -import { HintName } from 'hints/hintName'; -import { OpType } from '../hintParamsSchema'; -import { Felt } from 'primitives/felt'; import { segmentArenaHandler } from 'builtins/segmentArena'; -import { Relocatable } from 'primitives/relocatable'; + +import { HintName } from 'hints/hintName'; +import { OpType } from 'hints/hintParamsSchema'; import { allocFelt252DictParser } from './allocFelt252Dict'; import { felt252DictEntryUpdateParser } from './felt252DictEntryUpdate'; -import { SegmentValue } from 'primitives/segmentValue'; const initSegmentArenaBuiltin = (vm: VirtualMachine) => { const info = [ diff --git a/src/hints/dict/felt252DictEntryUpdate.ts b/src/hints/dict/felt252DictEntryUpdate.ts index 275ff26a..754a8d8b 100644 --- a/src/hints/dict/felt252DictEntryUpdate.ts +++ b/src/hints/dict/felt252DictEntryUpdate.ts @@ -1,10 +1,12 @@ import { z } from 'zod'; +import { ExpectedFelt } from 'errors/primitives'; + +import { isFelt } from 'primitives/segmentValue'; import { VirtualMachine } from 'vm/virtualMachine'; -import { Deref, OpType, resOp, ResOp } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; -import { isFelt } from 'primitives/segmentValue'; -import { ExpectedFelt } from 'errors/primitives'; +import { Deref, OpType, resOp, ResOp } from 'hints/hintParamsSchema'; /** Zod object to parse Felt252DictEntryUpdate hint */ export const felt252DictEntryUpdateParser = z diff --git a/src/hints/dict/getCurrentAccessDelta.test.ts b/src/hints/dict/getCurrentAccessDelta.test.ts index 9aa06070..65440609 100644 --- a/src/hints/dict/getCurrentAccessDelta.test.ts +++ b/src/hints/dict/getCurrentAccessDelta.test.ts @@ -1,9 +1,10 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; import { getCurrentAccessDeltaParser } from './getCurrentAccessDelta'; const GET_CURRENT_ACCESS_INDEX = { diff --git a/src/hints/dict/getCurrentAccessDelta.ts b/src/hints/dict/getCurrentAccessDelta.ts index cf7d55e7..4bd5eca0 100644 --- a/src/hints/dict/getCurrentAccessDelta.ts +++ b/src/hints/dict/getCurrentAccessDelta.ts @@ -1,9 +1,10 @@ import { z } from 'zod'; +import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; -import { cellRef, CellRef } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; /** Zod object to parse GetCurrentAccessDelta hint */ export const getCurrentAccessDeltaParser = z diff --git a/src/hints/dict/getCurrentAccessIndex.test.ts b/src/hints/dict/getCurrentAccessIndex.test.ts index 7d5e8e2b..5912db53 100644 --- a/src/hints/dict/getCurrentAccessIndex.test.ts +++ b/src/hints/dict/getCurrentAccessIndex.test.ts @@ -1,12 +1,13 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; +import { rangeCheckHandler } from 'builtins/rangeCheck'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; -import { getCurrentAccessIndexParser } from './getCurrentAccessIndex'; import { OpType } from 'hints/hintParamsSchema'; -import { rangeCheckHandler } from 'builtins/rangeCheck'; +import { getCurrentAccessIndexParser } from './getCurrentAccessIndex'; const GET_CURRENT_ACCESS_INDEX = { GetCurrentAccessIndex: { diff --git a/src/hints/dict/getCurrentAccessIndex.ts b/src/hints/dict/getCurrentAccessIndex.ts index 95d23293..a08ddc9e 100644 --- a/src/hints/dict/getCurrentAccessIndex.ts +++ b/src/hints/dict/getCurrentAccessIndex.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; + import { resOp, ResOp } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; diff --git a/src/hints/dict/getNextDictKey.test.ts b/src/hints/dict/getNextDictKey.test.ts index a5f21b93..179c6d53 100644 --- a/src/hints/dict/getNextDictKey.test.ts +++ b/src/hints/dict/getNextDictKey.test.ts @@ -1,9 +1,10 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; import { getNextDictKeyParser } from './getNextDictKey'; const GET_NEXT_DICT_KEY = { diff --git a/src/hints/dict/getNextDictKey.ts b/src/hints/dict/getNextDictKey.ts index f0c2b109..a5ef85a0 100644 --- a/src/hints/dict/getNextDictKey.ts +++ b/src/hints/dict/getNextDictKey.ts @@ -1,8 +1,9 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; -import { cellRef, CellRef } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; +import { cellRef, CellRef } from 'hints/hintParamsSchema'; /** Zod object to parse GetNextDictKey hint */ export const getNextDictKeyParser = z diff --git a/src/hints/dict/getSegmentArenaIndex.test.ts b/src/hints/dict/getSegmentArenaIndex.test.ts index ef1e1e7a..7e4386b8 100644 --- a/src/hints/dict/getSegmentArenaIndex.test.ts +++ b/src/hints/dict/getSegmentArenaIndex.test.ts @@ -1,12 +1,13 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; +import { Relocatable } from 'primitives/relocatable'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; +import { segmentArenaHandler } from 'builtins/segmentArena'; + import { HintName } from 'hints/hintName'; import { OpType } from '../hintParamsSchema'; -import { Felt } from 'primitives/felt'; -import { segmentArenaHandler } from 'builtins/segmentArena'; -import { Relocatable } from 'primitives/relocatable'; import { getSegmentArenaIndexParser } from './getSegmentArenaIndex'; import { allocFelt252DictParser } from './allocFelt252Dict'; diff --git a/src/hints/dict/getSegmentArenaIndex.ts b/src/hints/dict/getSegmentArenaIndex.ts index 26d38e70..3b03f567 100644 --- a/src/hints/dict/getSegmentArenaIndex.ts +++ b/src/hints/dict/getSegmentArenaIndex.ts @@ -1,8 +1,9 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; -import { resOp, ResOp, cellRef, CellRef } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; +import { resOp, ResOp, cellRef, CellRef } from 'hints/hintParamsSchema'; /** Zod object to parse GetSegmentArenaIndex hint */ export const getSegmentArenaIndexParser = z diff --git a/src/hints/dict/initSquashData.test.ts b/src/hints/dict/initSquashData.test.ts index cf9465be..bb612bb4 100644 --- a/src/hints/dict/initSquashData.test.ts +++ b/src/hints/dict/initSquashData.test.ts @@ -1,15 +1,16 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; +import { Relocatable } from 'primitives/relocatable'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; +import { segmentArenaHandler } from 'builtins/segmentArena'; + import { HintName } from 'hints/hintName'; -import { initSquashDataParser } from './initSquashData'; import { OpType } from 'hints/hintParamsSchema'; -import { Felt } from 'primitives/felt'; -import { segmentArenaHandler } from 'builtins/segmentArena'; -import { allocFelt252DictParser } from './allocFelt252Dict'; -import { Relocatable } from 'primitives/relocatable'; import { SquashedDictManager } from 'hints/squashedDictManager'; +import { allocFelt252DictParser } from './allocFelt252Dict'; +import { initSquashDataParser } from './initSquashData'; const initSegmentArenaBuiltin = (vm: VirtualMachine) => { const info = [ diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index ffc5395e..df2eef58 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -1,11 +1,13 @@ import { z } from 'zod'; -import { VirtualMachine } from 'vm/virtualMachine'; -import { CellRef, cellRef, resOp, ResOp } from 'hints/hintParamsSchema'; -import { HintName } from 'hints/hintName'; -import { isFelt } from 'primitives/segmentValue'; import { ExpectedFelt } from 'errors/primitives'; + import { Felt } from 'primitives/felt'; +import { isFelt } from 'primitives/segmentValue'; +import { VirtualMachine } from 'vm/virtualMachine'; + +import { HintName } from 'hints/hintName'; +import { CellRef, cellRef, resOp, ResOp } from 'hints/hintParamsSchema'; /** Zod object to parse InitSquashData hint */ export const initSquashDataParser = z diff --git a/src/hints/dict/shouldContinueSquashLoop.test.ts b/src/hints/dict/shouldContinueSquashLoop.test.ts index deb8331c..dafb2867 100644 --- a/src/hints/dict/shouldContinueSquashLoop.test.ts +++ b/src/hints/dict/shouldContinueSquashLoop.test.ts @@ -1,9 +1,10 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; import { shouldContinueSquashLoopParser } from './shouldContinueSquashLoop'; const SHOULD_CONTINUE_SQUASH_LOOP = { diff --git a/src/hints/dict/shouldContinueSquashLoop.ts b/src/hints/dict/shouldContinueSquashLoop.ts index bb8e4c25..6597acf8 100644 --- a/src/hints/dict/shouldContinueSquashLoop.ts +++ b/src/hints/dict/shouldContinueSquashLoop.ts @@ -1,9 +1,10 @@ import { z } from 'zod'; +import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; + import { cellRef, CellRef } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; /** Zod object to parse ShouldContinueSquashLoop hint */ export const shouldContinueSquashLoopParser = z diff --git a/src/hints/dict/shouldSkipSquashLoop.test.ts b/src/hints/dict/shouldSkipSquashLoop.test.ts index 70105a17..e9092daf 100644 --- a/src/hints/dict/shouldSkipSquashLoop.test.ts +++ b/src/hints/dict/shouldSkipSquashLoop.test.ts @@ -1,9 +1,10 @@ import { describe, expect, test } from 'bun:test'; +import { Felt } from 'primitives/felt'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; + import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; import { shouldSkipSquashLoopParser } from './shouldSkipSquashLoop'; const SHOULD_SKIP_SQUASH_LOOP = { diff --git a/src/hints/dict/shouldSkipSquashLoop.ts b/src/hints/dict/shouldSkipSquashLoop.ts index 932ef27d..1e7d954d 100644 --- a/src/hints/dict/shouldSkipSquashLoop.ts +++ b/src/hints/dict/shouldSkipSquashLoop.ts @@ -1,9 +1,10 @@ import { z } from 'zod'; +import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; + import { cellRef, CellRef } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; -import { Felt } from 'primitives/felt'; /** Zod object to parse ShouldSkipSquashLoop hint */ export const shouldSkipSquashLoopParser = z diff --git a/src/hints/hintHandler.ts b/src/hints/hintHandler.ts index 071846c2..37a9a307 100644 --- a/src/hints/hintHandler.ts +++ b/src/hints/hintHandler.ts @@ -1,4 +1,8 @@ import { VirtualMachine } from 'vm/virtualMachine'; + +import { Hint } from './hintSchema'; +import { HintName } from './hintName'; + import { AllocSegment, allocSegment } from './allocSegment'; import { AssertLeFindSmallArcs, @@ -43,8 +47,6 @@ import { ShouldSkipSquashLoop, shouldSkipSquashLoop, } from './dict/shouldSkipSquashLoop'; -import { HintName } from './hintName'; -import { Hint } from './hintSchema'; import { TestLessThan, testLessThan } from './math/testLessThan'; export const handlers: Record< diff --git a/src/hints/hintParamsSchema.test.ts b/src/hints/hintParamsSchema.test.ts index 594bf19c..4530a114 100644 --- a/src/hints/hintParamsSchema.test.ts +++ b/src/hints/hintParamsSchema.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test } from 'bun:test'; import { Felt } from 'primitives/felt'; import { Register } from 'vm/instruction'; + import { CellRef, OpType, diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index c2212798..892737db 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; + import { allocSegmentParser } from './allocSegment'; import { assertLeFindSmallArcsParser } from './assertLeFindSmallArc'; import { assertLeIsFirstArcExcludedParser } from './assertLeIsFirstArcExcluded'; diff --git a/src/hints/math/testLessThan.test.ts b/src/hints/math/testLessThan.test.ts index 93b26fac..63ef7de9 100644 --- a/src/hints/math/testLessThan.test.ts +++ b/src/hints/math/testLessThan.test.ts @@ -3,6 +3,7 @@ import { describe, expect, test } from 'bun:test'; import { Felt } from 'primitives/felt'; import { Register } from 'vm/instruction'; import { VirtualMachine } from 'vm/virtualMachine'; + import { OpType } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; import { testLessThanParser } from './testLessThan'; diff --git a/src/hints/math/testLessThan.ts b/src/hints/math/testLessThan.ts index 95e478da..1174e956 100644 --- a/src/hints/math/testLessThan.ts +++ b/src/hints/math/testLessThan.ts @@ -2,8 +2,9 @@ import { z } from 'zod'; import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; -import { cellRef, resOp, CellRef, ResOp } from 'hints/hintParamsSchema'; + import { HintName } from 'hints/hintName'; +import { cellRef, resOp, CellRef, ResOp } from 'hints/hintParamsSchema'; /** Zod object to parse TestLessThan hint */ export const testLessThanParser = z diff --git a/src/hints/scopeManager.test.ts b/src/hints/scopeManager.test.ts index 2c2c7468..76778d36 100644 --- a/src/hints/scopeManager.test.ts +++ b/src/hints/scopeManager.test.ts @@ -1,7 +1,9 @@ import { describe, expect, test } from 'bun:test'; -import { ScopeManager } from './scopeManager'; + import { CannotExitMainScope, VariableNotInScope } from 'errors/scopeManager'; + import { Felt } from 'primitives/felt'; +import { ScopeManager } from './scopeManager'; describe('ScopeManager', () => { test('constructor', () => { diff --git a/src/hints/squashedDictManager.test.ts b/src/hints/squashedDictManager.test.ts index 66442d24..372cba67 100644 --- a/src/hints/squashedDictManager.test.ts +++ b/src/hints/squashedDictManager.test.ts @@ -1,8 +1,10 @@ import { describe, expect, test } from 'bun:test'; -import { SquashedDictManager } from './squashedDictManager'; -import { Felt } from 'primitives/felt'; + import { EmptyKeys } from 'errors/squashedDict'; +import { Felt } from 'primitives/felt'; +import { SquashedDictManager } from './squashedDictManager'; + describe('SquashedDictManager', () => { test('should properly initialize a SquashedDictManager', () => { const squashedDictManager = new SquashedDictManager(); diff --git a/src/hints/squashedDictManager.ts b/src/hints/squashedDictManager.ts index b2b37830..e9943c8b 100644 --- a/src/hints/squashedDictManager.ts +++ b/src/hints/squashedDictManager.ts @@ -1,4 +1,5 @@ import { EmptyIndex, EmptyIndices, EmptyKeys } from 'errors/squashedDict'; + import { Felt } from 'primitives/felt'; export class SquashedDictManager { From 0ef9b079c9debe61c76b761354dedf271053c198 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 17:34:00 +0200 Subject: [PATCH 42/78] chore: add missing JSDoc to errors --- src/errors/scopeManager.ts | 2 ++ src/errors/{squashedDict.ts => squashedDictManager.ts} | 9 ++++++--- src/hints/squashedDictManager.test.ts | 2 +- src/hints/squashedDictManager.ts | 6 +++++- 4 files changed, 14 insertions(+), 5 deletions(-) rename src/errors/{squashedDict.ts => squashedDictManager.ts} (55%) diff --git a/src/errors/scopeManager.ts b/src/errors/scopeManager.ts index df7796e0..36f703d4 100644 --- a/src/errors/scopeManager.ts +++ b/src/errors/scopeManager.ts @@ -1,11 +1,13 @@ class ScopeManagerError extends Error {} +/** The main scope cannot be removed, there must always be at least one scope. */ export class CannotExitMainScope extends ScopeManagerError { constructor() { super('Cannot exit the main scope'); } } +/** The variable `name` is not accessible in the current scope. */ export class VariableNotInScope extends ScopeManagerError { constructor(name: string) { super(`Variable ${name} is not in scope`); diff --git a/src/errors/squashedDict.ts b/src/errors/squashedDictManager.ts similarity index 55% rename from src/errors/squashedDict.ts rename to src/errors/squashedDictManager.ts index 5691fc8c..db2b671b 100644 --- a/src/errors/squashedDict.ts +++ b/src/errors/squashedDictManager.ts @@ -2,24 +2,27 @@ import { Felt } from 'primitives/felt'; class SquashedDictManagerError extends Error {} +/** There is no keys in the squashedDictionnaryManager */ export class EmptyKeys extends SquashedDictManagerError { constructor() { - super('There is no keys left in the squashed dictionnary.'); + super('There is no keys left in the squashed dictionnary manager.'); } } +/** There is no indices at `key` */ export class EmptyIndices extends SquashedDictManagerError { constructor(key: Felt | undefined) { super( `There is no indices at key ${ key ? key.toString() : key - } in the squashed dictionnary.` + } in the squashed dictionnary manager.` ); } } +/** The last index of the squashed dictionnary manager is empty. */ export class EmptyIndex extends SquashedDictManagerError { constructor() { - super('The last index of the squashed dictionnary is empty.'); + super('The last index of the squashed dictionnary manager is empty.'); } } diff --git a/src/hints/squashedDictManager.test.ts b/src/hints/squashedDictManager.test.ts index 372cba67..a9296369 100644 --- a/src/hints/squashedDictManager.test.ts +++ b/src/hints/squashedDictManager.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'bun:test'; -import { EmptyKeys } from 'errors/squashedDict'; +import { EmptyKeys } from 'errors/squashedDictManager'; import { Felt } from 'primitives/felt'; import { SquashedDictManager } from './squashedDictManager'; diff --git a/src/hints/squashedDictManager.ts b/src/hints/squashedDictManager.ts index e9943c8b..992c8edd 100644 --- a/src/hints/squashedDictManager.ts +++ b/src/hints/squashedDictManager.ts @@ -1,4 +1,8 @@ -import { EmptyIndex, EmptyIndices, EmptyKeys } from 'errors/squashedDict'; +import { + EmptyIndex, + EmptyIndices, + EmptyKeys, +} from 'errors/squashedDictManager'; import { Felt } from 'primitives/felt'; From 71c64890d370678182ad231aba41b2c7c620234e Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 17:37:52 +0200 Subject: [PATCH 43/78] chore: remove useless type assertion --- src/vm/virtualMachine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index a0bfd21f..58f8188c 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -600,7 +600,7 @@ export class VirtualMachine { switch (binOp.op) { case Operation.Add: - return a.add(b) as Felt; + return a.add(b); case Operation.Mul: return a.mul(b); From fbd9210e8dd8c39de2a265915ad26d4ea33c3802 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 17:43:38 +0200 Subject: [PATCH 44/78] doc: add JSDoc to SquashedDictManager --- src/hints/squashedDictManager.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/hints/squashedDictManager.ts b/src/hints/squashedDictManager.ts index 992c8edd..7d0d27aa 100644 --- a/src/hints/squashedDictManager.ts +++ b/src/hints/squashedDictManager.ts @@ -6,8 +6,13 @@ import { import { Felt } from 'primitives/felt'; +/** + * Handle the squashing of dictionnaries. + */ export class SquashedDictManager { + /** Maps the key of a dictionnary to its taken values accross the run. */ public keyToIndices: Map; + /** An array containing the keys that still needs to be squashed. */ public keys: Felt[]; constructor() { @@ -15,6 +20,7 @@ export class SquashedDictManager { this.keys = []; } + /** Add `index` to the indices taken by `key` */ insert(key: Felt, index: Felt) { const keyStr = key.toString(); const indices = this.keyToIndices.get(keyStr); @@ -25,18 +31,21 @@ export class SquashedDictManager { } } + /** Return the last key of the dictionnary. */ lastKey(): Felt { const len = this.keys.length; if (!len) throw new EmptyKeys(); return this.keys[len - 1]; } + /** Remove and return the last key of the dictionnary. */ popKey(): Felt { const key = this.keys.pop(); if (!key) throw new EmptyKeys(); return key; } + /** Return the array of indices taken by the last key. */ lastIndices(): Felt[] { const key = this.lastKey(); const indices = this.keyToIndices.get(key.toString()); @@ -44,6 +53,7 @@ export class SquashedDictManager { return indices; } + /** Return the last index of the indices taken by the last key. */ lastIndex(): Felt { const indices = this.lastIndices(); const len = indices.length; @@ -51,6 +61,7 @@ export class SquashedDictManager { return indices[len - 1]; } + /** Remove and return the last index of the indices taken by the last key. */ popIndex(): Felt { const index = this.lastIndices().pop(); if (!index) throw new EmptyIndex(); From 616a4d89492092b03131343977ce055f14434a5f Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 17:51:31 +0200 Subject: [PATCH 45/78] doc: add hint dev in progress --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f40f0dd4..ca2dabc7 100644 --- a/README.md +++ b/README.md @@ -165,14 +165,16 @@ You can still add it as a dependency with a local copy: | [Keccak](https://github.com/kkrt-labs/cairo-vm-ts/issues/69) | ☑ | | [Poseidon](https://github.com/kkrt-labs/cairo-vm-ts/issues/71) | ☑ | | [Range Check 96](https://github.com/kkrt-labs/cairo-vm-ts/issues/81) | ☑ | -| Segment Arena | ☐ | +| [Segment Arena](https://github.com/kkrt-labs/cairo-vm-ts/pull/106) | ☑ | | AddMod | ☐ | | MulMod | ☐ | ### Hints - - +Hints are currently being implemented. + +Their development can be tracked +[here](https://github.com/kkrt-labs/cairo-vm-ts/issues/90). ### Differential Testing & Benchmark From 275885eeba18f2424a5158c51d422a34d4c79a20 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 18:01:55 +0200 Subject: [PATCH 46/78] refactor: use forEach to execute hints --- src/vm/virtualMachine.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 58f8188c..5a17ffb0 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -108,9 +108,7 @@ export class VirtualMachine { * - Run the instruction */ step(hints?: Hint[]): void { - if (hints) { - hints.map((hint) => this.executeHint(hint)); - } + hints?.forEach((hint) => this.executeHint(hint)); const maybeEncodedInstruction = this.memory.get(this.pc); if (maybeEncodedInstruction === undefined) { throw new UndefinedInstruction(this.pc); From 0f190ed68cde1d72a98b2bc9403b0197065643a3 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 18:52:18 +0200 Subject: [PATCH 47/78] refactor: use number to add and sub felt when possible --- src/hints/dict/getCurrentAccessDelta.test.ts | 4 +--- src/hints/dict/getCurrentAccessDelta.ts | 2 +- src/hints/dict/initSquashData.test.ts | 16 ++++++--------- src/primitives/felt.test.ts | 12 +++++++++++ src/primitives/felt.ts | 21 +++++++++++++------- src/primitives/segmentValue.ts | 2 +- 6 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/hints/dict/getCurrentAccessDelta.test.ts b/src/hints/dict/getCurrentAccessDelta.test.ts index 65440609..82c5a1d4 100644 --- a/src/hints/dict/getCurrentAccessDelta.test.ts +++ b/src/hints/dict/getCurrentAccessDelta.test.ts @@ -43,8 +43,6 @@ describe('GetCurrentAccessDelta', () => { vm.executeHint(hint); - expect(vm.memory.get(vm.ap)).toEqual( - lastIndex.sub(poppedIndex).sub(new Felt(1n)) - ); + expect(vm.memory.get(vm.ap)).toEqual(lastIndex.sub(poppedIndex).sub(1)); }); }); diff --git a/src/hints/dict/getCurrentAccessDelta.ts b/src/hints/dict/getCurrentAccessDelta.ts index 4bd5eca0..20d564b1 100644 --- a/src/hints/dict/getCurrentAccessDelta.ts +++ b/src/hints/dict/getCurrentAccessDelta.ts @@ -34,6 +34,6 @@ export const getCurrentAccessDelta = ( vm.memory.assertEq( vm.cellRefToRelocatable(indexDeltaMinusOne), - currIndex.sub(prevIndex).sub(new Felt(1n)) + currIndex.sub(prevIndex).sub(1) ); }; diff --git a/src/hints/dict/initSquashData.test.ts b/src/hints/dict/initSquashData.test.ts index bb612bb4..71ef3acd 100644 --- a/src/hints/dict/initSquashData.test.ts +++ b/src/hints/dict/initSquashData.test.ts @@ -131,21 +131,17 @@ describe('InitSquashData', () => { values.reduce((prev, curr, i) => { const index = i - 1; - const keyIndex = new Felt(BigInt(index * dictAccessSize)); - const prevIndex = new Felt(BigInt(index * dictAccessSize + 1)); - const currIndex = new Felt(BigInt(index * dictAccessSize + 2)); - const prevValue = prev !== undefined ? prev : new Felt(0n); - vm.memory.assertEq(dictPtr.add(keyIndex), key); - vm.memory.assertEq(dictPtr.add(prevIndex), prevValue); - vm.memory.assertEq(dictPtr.add(currIndex), curr); + vm.memory.assertEq(dictPtr.add(index * dictAccessSize), key); + vm.memory.assertEq(dictPtr.add(index * dictAccessSize + 1), prev); + vm.memory.assertEq(dictPtr.add(index * dictAccessSize + 2), curr); return curr; }); const nAccesses = values.length - 1; const ptrDiff = nAccesses * dictAccessSize; - vm.memory.assertEq(vm.ap.add(new Felt(1n)), dictPtr); - vm.memory.assertEq(vm.ap.add(new Felt(2n)), new Felt(BigInt(ptrDiff))); - vm.memory.assertEq(vm.ap.add(new Felt(3n)), new Felt(BigInt(nAccesses))); + vm.memory.assertEq(vm.ap.add(1), dictPtr); + vm.memory.assertEq(vm.ap.add(2), new Felt(BigInt(ptrDiff))); + vm.memory.assertEq(vm.ap.add(3), new Felt(BigInt(nAccesses))); vm.executeHint(hint); diff --git a/src/primitives/felt.test.ts b/src/primitives/felt.test.ts index 77f54a10..1c24c41f 100644 --- a/src/primitives/felt.test.ts +++ b/src/primitives/felt.test.ts @@ -72,6 +72,12 @@ describe('Felt', () => { const expected = new Felt(1n); expect(result.eq(expected)).toBeTrue(); }); + test('should add a felt and a number properly', () => { + const a = new Felt(10n); + const b = 20; + const expected = new Felt(30n); + expect(a.add(b)).toEqual(expected); + }); }); describe('sub', () => { @@ -89,6 +95,12 @@ describe('Felt', () => { const expected = new Felt(Felt.PRIME - 3n); expect(result.eq(expected)).toBeTrue(); }); + test('should sub a felt and a number properly', () => { + const a = new Felt(10n); + const b = 20; + const expected = new Felt(-10n); + expect(a.sub(b)).toEqual(expected); + }); }); describe('mul', () => { diff --git a/src/primitives/felt.ts b/src/primitives/felt.ts index 558c041c..43aaac9c 100644 --- a/src/primitives/felt.ts +++ b/src/primitives/felt.ts @@ -31,28 +31,35 @@ export class Felt { } add(other: Felt): Felt; + add(other: number): Felt; add(other: Relocatable): Relocatable; add(other: SegmentValue): SegmentValue; - add(other: SegmentValue): SegmentValue { + add(other: SegmentValue | number): SegmentValue { if (isRelocatable(other)) { return other.add(this); } - if (!isFelt(other)) { - throw new ExpectedFelt(other); + + if (isFelt(other)) { + return new Felt(this.inner + other.inner); } - return new Felt(this.inner + other.inner); + return new Felt(this.inner + BigInt(other)); } sub(other: Felt): Felt; + sub(other: number): Felt; sub(other: Relocatable): never; sub(other: SegmentValue): SegmentValue; - sub(other: SegmentValue): SegmentValue { - if (!isFelt(other)) { + sub(other: SegmentValue | number): SegmentValue { + if (isFelt(other)) { + return new Felt(this.inner - other.inner); + } + + if (isRelocatable(other)) { throw new ExpectedFelt(other); } - return new Felt(this.inner - other.inner); + return new Felt(this.inner - BigInt(other)); } mul(other: SegmentValue): Felt { diff --git a/src/primitives/segmentValue.ts b/src/primitives/segmentValue.ts index e1cbeb88..6126c46a 100644 --- a/src/primitives/segmentValue.ts +++ b/src/primitives/segmentValue.ts @@ -17,7 +17,7 @@ export function isRelocatable( segmentValue: SegmentValue ): segmentValue is Relocatable; export function isRelocatable( - segmentValue: Relocatable | number + segmentValue: SegmentValue | number ): segmentValue is Relocatable; export function isRelocatable( segmentValue: SegmentValue | number From 767bc8de507c6c2bc29f5ebe0a2cc1724a0d9fb1 Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 19:23:35 +0200 Subject: [PATCH 48/78] chore: enforce camelCase in hint fields --- src/hints/dict/getSegmentArenaIndex.ts | 4 ++-- src/hints/hintHandler.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hints/dict/getSegmentArenaIndex.ts b/src/hints/dict/getSegmentArenaIndex.ts index 3b03f567..d8f0a442 100644 --- a/src/hints/dict/getSegmentArenaIndex.ts +++ b/src/hints/dict/getSegmentArenaIndex.ts @@ -15,8 +15,8 @@ export const getSegmentArenaIndexParser = z }) .transform(({ GetSegmentArenaIndex: { dict_end_ptr, dict_index } }) => ({ type: HintName.GetSegmentArenaIndex, - dict_end_ptr, - dict_index, + dictEndptr: dict_end_ptr, + dictIndex: dict_index, })); /** diff --git a/src/hints/hintHandler.ts b/src/hints/hintHandler.ts index 37a9a307..58b8926d 100644 --- a/src/hints/hintHandler.ts +++ b/src/hints/hintHandler.ts @@ -95,7 +95,7 @@ export const handlers: Record< }, [HintName.GetSegmentArenaIndex]: (vm, hint) => { const h = hint as GetSegmentArenaIndex; - getSegmentArenaIndex(vm, h.dict_end_ptr, h.dict_index); + getSegmentArenaIndex(vm, h.dictEndptr, h.dictIndex); }, [HintName.InitSquashData]: (vm, hint) => { const h = hint as InitSquashData; From 883f94de1b7cca92d25677c9267c7d7b5a18fdfd Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 19:56:35 +0200 Subject: [PATCH 49/78] doc: add hint implementation how-to --- README.md | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/README.md b/README.md index ca2dabc7..26009ab1 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,174 @@ Hints are currently being implemented. Their development can be tracked [here](https://github.com/kkrt-labs/cairo-vm-ts/issues/90). +#### How to implement a hint ? + +Here is a how-to, using the hint `GetSegmentArenaIndex` as an example: + +##### Find the signature + +Find the signature of the hint in the +[Cairo compiler](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-casm/src/hints/mod.rs): +[GetSegmentArenaIndex](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-casm/src/hints/mod.rs#L203) + +```rust +/// Retrieves the index of the given dict in the dict_infos segment. +GetSegmentArenaIndex { dict_end_ptr: ResOperand, dict_index: CellRef }, +``` + +Here, `dict_end_ptr` is a `ResOp` while `dict_index` is a `CellRef`. + +The definitions of `Cellref` and `ResOp` can be found in `hintParamSchema.ts`. +Hint arguments can only be one of these two types. + +##### Parsing + +The Cairo VM takes the serialized compilation artifacts as input, we use Zod to +parse them. Each hint has its own parser object. + +The GetSegmentArenaIndex hint can be found in a format similar to this one: + +```json +"GetSegmentArenaIndex": { + "dict_end_ptr": { + "Deref": { + "register": "FP", + "offset": -3 + } + }, + "dict_index": { + "register": "FP", + "offset": 0 + } +} +``` + +- Add `GetSegmentArenaIndex` to `HintName` enum in `src/hints/hintName.ts`. + + It is used to identify the hint before executing it in a run. + + Hints are ordered in an ascending alphabetical order. + + ```typescript + // hintName.ts + export enum HintName { + // ... + GetSegmentArenaIndex = 'GetSegmentArenaIndex', + } + ``` + +- Create the file `src/hints/dict/getSegmentArenaIndex.ts` (in a `dict/` + sub-folder because the hint is related to dictionnaries). + +- Create and export a Zod object `getSegmentArenaIndexParser` which follows the + hint signature: + + ```typescript + // getSegmentArenaIndex.ts + export const getSegmentArenaIndexParser = z + .object({ + GetSegmentArenaIndex: z.object({ + dict_end_ptr: resOp, + dict_index: cellRef, + }), + }) + .transform(({ GetSegmentArenaIndex: { dict_end_ptr, dict_index } }) => ({ + type: HintName.GetSegmentArenaIndex, + dictEndPtr: dict_end_ptr, + dictIndex: dict_index, + })); + ``` + + The parsed object must be transformed in two ways: + + 1. Enforce camelCase in fields name + 2. Add a field `type` which takes the corresponding value of the `HintName` + enum. + +- Add the parser to the Zod union `hint` in `src/hints/hintSchema.ts`: + + ```typescript + // hintSchema.ts + const hint = z.union([ + // ... + getSegmentArenaIndexParser, + ]); + ``` + +Now, we can implement the core logic of the hint. + +##### Core Logic + +The core logic of the hint will be implemented in the same file as the hint +parser, here `getSegmentArenaIndex.ts`. The function implementing this logic +must be named as the camelCase version of the hint: `getSegmentArenaIndex()` +(similar to its filename). + +The parameters of the function are: + +- `vm: VirtualMachine`, the virtual machine as the hint must interact with it in + different ways (e.g. read from memory) +- Signature of the hint. + +So, in our case, the function signature would be + +```typescript +export getSegmentArenaIndex(vm: VirtualMachine, dictEndPtr: ResOp, dictIndex: CellRef) { + // Core logic of GetSegmentArenaIndex hint +} +``` + +To implement the logic, refer yourself to its implementation in the +[`cairo-vm`](https://github.com/lambdaclass/cairo-vm/blob/24c2349cc19832fd8c1552304fe0439765ed82c6/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs#L427-L444) +and the +[`cairo-lang-runner`](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-runner/src/casm_run/mod.rs#L1873-L1880) +from the Cairo compiler. + +The last step before testing is adding the hint to the handler object. + +##### Handler + +The handler is defined in `src/hints/hintHandler.ts` + +It is a dictionnary which maps a `HintName` value to the core logic function. + +```typescript +export const handlers: Record< + HintName, + (vm: VirtualMachine, hint: Hint) => void +> = { + [HintName.GetSegmentArenaIndex]: (vm, hint) => { + const h = hint as GetSegmentArenaIndex; + getSegmentArenaIndex(vm, h.dictEndptr, h.dictIndex); + }, +}; +``` + +- Set the key as the HintName value, `HintName.GetSegmentArenaIndex` +- Set the value to a function which takes `(vm, hint)` as parameters and execute + the core logic function of the corresponding hint. + +To do so, we make a type assertion of the hint, matching the `HintName` value, +and we call the corresponding core logic function with the appropriate +arguments. + +The hint has been implemented, the last thing to do is testing it. + +##### Testing + +Unit tests must be done, testing the correct parsing of the hint and the +execution of the core logic. Those tests are done in a `.test.ts` file in the +same folder as the hint. In our example, it would be +`src/hints/dict/getSegmentArenaIndex.test.ts`. + +Finally, a Cairo program using this hint must be created in +`cairo_programs/cairo/hints`. Verify its proper execution by compiling it with +`make compile` and run the command + +```bash +cairo run path/to/my_program.json +``` + ### Differential Testing & Benchmark Pre-requisite: `make` From c157c80eb63cfec78f9943dda1b01ca4fd3405df Mon Sep 17 00:00:00 2001 From: malatrax Date: Tue, 23 Jul 2024 20:06:33 +0200 Subject: [PATCH 50/78] refactor: export how to guide to a specific file in hints folder --- README.md | 166 +------------------------------ src/hints/howToImplementAHint.md | 155 +++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 165 deletions(-) create mode 100644 src/hints/howToImplementAHint.md diff --git a/README.md b/README.md index 26009ab1..f24013f4 100644 --- a/README.md +++ b/README.md @@ -178,171 +178,7 @@ Their development can be tracked #### How to implement a hint ? -Here is a how-to, using the hint `GetSegmentArenaIndex` as an example: - -##### Find the signature - -Find the signature of the hint in the -[Cairo compiler](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-casm/src/hints/mod.rs): -[GetSegmentArenaIndex](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-casm/src/hints/mod.rs#L203) - -```rust -/// Retrieves the index of the given dict in the dict_infos segment. -GetSegmentArenaIndex { dict_end_ptr: ResOperand, dict_index: CellRef }, -``` - -Here, `dict_end_ptr` is a `ResOp` while `dict_index` is a `CellRef`. - -The definitions of `Cellref` and `ResOp` can be found in `hintParamSchema.ts`. -Hint arguments can only be one of these two types. - -##### Parsing - -The Cairo VM takes the serialized compilation artifacts as input, we use Zod to -parse them. Each hint has its own parser object. - -The GetSegmentArenaIndex hint can be found in a format similar to this one: - -```json -"GetSegmentArenaIndex": { - "dict_end_ptr": { - "Deref": { - "register": "FP", - "offset": -3 - } - }, - "dict_index": { - "register": "FP", - "offset": 0 - } -} -``` - -- Add `GetSegmentArenaIndex` to `HintName` enum in `src/hints/hintName.ts`. - - It is used to identify the hint before executing it in a run. - - Hints are ordered in an ascending alphabetical order. - - ```typescript - // hintName.ts - export enum HintName { - // ... - GetSegmentArenaIndex = 'GetSegmentArenaIndex', - } - ``` - -- Create the file `src/hints/dict/getSegmentArenaIndex.ts` (in a `dict/` - sub-folder because the hint is related to dictionnaries). - -- Create and export a Zod object `getSegmentArenaIndexParser` which follows the - hint signature: - - ```typescript - // getSegmentArenaIndex.ts - export const getSegmentArenaIndexParser = z - .object({ - GetSegmentArenaIndex: z.object({ - dict_end_ptr: resOp, - dict_index: cellRef, - }), - }) - .transform(({ GetSegmentArenaIndex: { dict_end_ptr, dict_index } }) => ({ - type: HintName.GetSegmentArenaIndex, - dictEndPtr: dict_end_ptr, - dictIndex: dict_index, - })); - ``` - - The parsed object must be transformed in two ways: - - 1. Enforce camelCase in fields name - 2. Add a field `type` which takes the corresponding value of the `HintName` - enum. - -- Add the parser to the Zod union `hint` in `src/hints/hintSchema.ts`: - - ```typescript - // hintSchema.ts - const hint = z.union([ - // ... - getSegmentArenaIndexParser, - ]); - ``` - -Now, we can implement the core logic of the hint. - -##### Core Logic - -The core logic of the hint will be implemented in the same file as the hint -parser, here `getSegmentArenaIndex.ts`. The function implementing this logic -must be named as the camelCase version of the hint: `getSegmentArenaIndex()` -(similar to its filename). - -The parameters of the function are: - -- `vm: VirtualMachine`, the virtual machine as the hint must interact with it in - different ways (e.g. read from memory) -- Signature of the hint. - -So, in our case, the function signature would be - -```typescript -export getSegmentArenaIndex(vm: VirtualMachine, dictEndPtr: ResOp, dictIndex: CellRef) { - // Core logic of GetSegmentArenaIndex hint -} -``` - -To implement the logic, refer yourself to its implementation in the -[`cairo-vm`](https://github.com/lambdaclass/cairo-vm/blob/24c2349cc19832fd8c1552304fe0439765ed82c6/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs#L427-L444) -and the -[`cairo-lang-runner`](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-runner/src/casm_run/mod.rs#L1873-L1880) -from the Cairo compiler. - -The last step before testing is adding the hint to the handler object. - -##### Handler - -The handler is defined in `src/hints/hintHandler.ts` - -It is a dictionnary which maps a `HintName` value to the core logic function. - -```typescript -export const handlers: Record< - HintName, - (vm: VirtualMachine, hint: Hint) => void -> = { - [HintName.GetSegmentArenaIndex]: (vm, hint) => { - const h = hint as GetSegmentArenaIndex; - getSegmentArenaIndex(vm, h.dictEndptr, h.dictIndex); - }, -}; -``` - -- Set the key as the HintName value, `HintName.GetSegmentArenaIndex` -- Set the value to a function which takes `(vm, hint)` as parameters and execute - the core logic function of the corresponding hint. - -To do so, we make a type assertion of the hint, matching the `HintName` value, -and we call the corresponding core logic function with the appropriate -arguments. - -The hint has been implemented, the last thing to do is testing it. - -##### Testing - -Unit tests must be done, testing the correct parsing of the hint and the -execution of the core logic. Those tests are done in a `.test.ts` file in the -same folder as the hint. In our example, it would be -`src/hints/dict/getSegmentArenaIndex.test.ts`. - -Finally, a Cairo program using this hint must be created in -`cairo_programs/cairo/hints`. Verify its proper execution by compiling it with -`make compile` and run the command - -```bash -cairo run path/to/my_program.json -``` +A how-to guide has been written [here](/src/hints/howToImplementAHint.md). ### Differential Testing & Benchmark diff --git a/src/hints/howToImplementAHint.md b/src/hints/howToImplementAHint.md new file mode 100644 index 00000000..52fc5c79 --- /dev/null +++ b/src/hints/howToImplementAHint.md @@ -0,0 +1,155 @@ +# How to implement a hint + +Here is a how-to, using the hint `GetSegmentArenaIndex` as an example: + +## Find the signature + +Find the signature of the hint in the +[Cairo compiler](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-casm/src/hints/mod.rs): +[GetSegmentArenaIndex](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-casm/src/hints/mod.rs#L203) + +```rust +/// Retrieves the index of the given dict in the dict_infos segment. +GetSegmentArenaIndex { dict_end_ptr: ResOperand, dict_index: CellRef }, +``` + +Here, `dict_end_ptr` is a `ResOp` while `dict_index` is a `CellRef`. + +The definitions of `Cellref` and `ResOp` can be found in `hintParamSchema.ts`. +Hint arguments can only be one of these two types. + +## Parsing + +The Cairo VM takes the serialized compilation artifacts as input, we use Zod to +parse them. Each hint has its own parser object. + +The GetSegmentArenaIndex hint can be found in a format similar to this one: + +```json +"GetSegmentArenaIndex": { + "dict_end_ptr": { + "Deref": { + "register": "FP", + "offset": -3 + } + }, + "dict_index": { + "register": "FP", + "offset": 0 + } +} +``` + +- Add `GetSegmentArenaIndex` to the `HintName` enum in `src/hints/hintName.ts`. + It is used to identify the hint before executing it in a run. Hints are + ordered in an ascending alphabetical order. + + ```typescript + // hintName.ts + export enum HintName { + // ... + GetSegmentArenaIndex = 'GetSegmentArenaIndex', + } + ``` + +- Create the file `src/hints/dict/getSegmentArenaIndex.ts`. Place the file in + the appropriate sub-folder category, here `dict` because the hint is dedicated + to dictionnaries. + +- Create and export a Zod object `getSegmentArenaIndexParser` which follows the + hint signature: + + ```typescript + // getSegmentArenaIndex.ts + export const getSegmentArenaIndexParser = z + .object({ + GetSegmentArenaIndex: z.object({ + dict_end_ptr: resOp, + dict_index: cellRef, + }), + }) + .transform(({ GetSegmentArenaIndex: { dict_end_ptr, dict_index } }) => ({ + type: HintName.GetSegmentArenaIndex, + dictEndPtr: dict_end_ptr, + dictIndex: dict_index, + })); + ``` + + The parsed object must be transformed in two ways: + + 1. Enforce camelCase in fields name + 2. Add a field `type` which takes the corresponding value of the `HintName` + enum. + +- Add the parser to the Zod union `hint` in `src/hints/hintSchema.ts`: + + ```typescript + // hintSchema.ts + const hint = z.union([ + // ... + getSegmentArenaIndexParser, + ]); + ``` + +Now, we can implement the core logic of the hint. + +## Core Logic + +The core logic of the hint will be implemented in the same file as the hint +parser, here `getSegmentArenaIndex.ts`. The function implementing this logic +must be named as the camelCase version of the hint: `getSegmentArenaIndex()` +(similar to its filename). + +The parameters of the function are the virtual machine, as the hint must +interact with it, and the signature of the hint. + +So, in our case, the function signature would be +`export getSegmentArenaIndex(vm: VirtualMachine, dictEndPtr: ResOp, dictIndex: CellRef)` + +To implement the logic, refer yourself to its implementation in the +[`cairo-vm`](https://github.com/lambdaclass/cairo-vm/blob/24c2349cc19832fd8c1552304fe0439765ed82c6/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs#L427-L444) +and the +[`cairo-lang-runner`](https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-runner/src/casm_run/mod.rs#L1873-L1880) +from the Cairo compiler. + +The last step is adding the hint to the handler object. + +## Handler + +The handler is defined in `src/hints/hintHandler.ts` + +It is a dictionnary which maps a `HintName` value to a function executing the +corresponding core logic function. + +```typescript +export const handlers: Record< + HintName, + (vm: VirtualMachine, hint: Hint) => void +> = { + [HintName.GetSegmentArenaIndex]: (vm, hint) => { + const h = hint as GetSegmentArenaIndex; + getSegmentArenaIndex(vm, h.dictEndptr, h.dictIndex); + }, +}; +``` + +- Set the key as the HintName value, `HintName.GetSegmentArenaIndex` +- Set the value to a function which takes `(vm, hint)` as parameters and execute + the core logic function of the corresponding hint. + +To do so, we make a type assertion of the hint, matching the `HintName` value, +and we call the corresponding core logic function with the appropriate +arguments. + +The hint has been implemented, the last thing to do is testing it. + +## Testing + +Unit tests must test the correct parsing of the hint and the execution of the +core logic. Those tests are done in a `.test.ts` file in the same folder as the +hint. In our example, it would be `src/hints/dict/getSegmentArenaIndex.test.ts`. + +Integration test is done by creating a Cairo program in +`cairo_programs/cairo/hints`. We must verify its proper execution by compiling +it with `make compile` and executing it with the command +`cairo run path/to/my_program.json` From 7c65ac83597032dfc2df2adbbc9659779a051c9d Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 24 Jul 2024 10:23:25 +0200 Subject: [PATCH 51/78] fix: correct camelCase instead of snake_case in GetSegmentArenaIndex fields --- src/hints/dict/getSegmentArenaIndex.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hints/dict/getSegmentArenaIndex.test.ts b/src/hints/dict/getSegmentArenaIndex.test.ts index 7e4386b8..4c09c37c 100644 --- a/src/hints/dict/getSegmentArenaIndex.test.ts +++ b/src/hints/dict/getSegmentArenaIndex.test.ts @@ -53,14 +53,14 @@ describe('GetSegmentArenaIndex', () => { const hint = getSegmentArenaIndexParser.parse(GET_SEGMENT_ARENA_INDEX); expect(hint).toEqual({ type: HintName.GetSegmentArenaIndex, - dict_end_ptr: { + dictEndptr: { type: OpType.Deref, cell: { register: Register.Ap, offset: 1, }, }, - dict_index: { + dictIndex: { register: Register.Ap, offset: 2, }, From f60ac6112e5c1b2df0e0ed3f2971d8608236f875 Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 24 Jul 2024 10:26:50 +0200 Subject: [PATCH 52/78] refactor: move how to to a docs folder --- README.md | 2 +- {src/hints => docs}/howToImplementAHint.md | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {src/hints => docs}/howToImplementAHint.md (100%) diff --git a/README.md b/README.md index f24013f4..4eec799b 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ Their development can be tracked #### How to implement a hint ? -A how-to guide has been written [here](/src/hints/howToImplementAHint.md). +A how-to guide has been written [here](/docs/howToImplementAHint.md). ### Differential Testing & Benchmark diff --git a/src/hints/howToImplementAHint.md b/docs/howToImplementAHint.md similarity index 100% rename from src/hints/howToImplementAHint.md rename to docs/howToImplementAHint.md From de8faf8a84844028d3a9098e91c9c7760c641a1b Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 24 Jul 2024 12:38:37 +0200 Subject: [PATCH 53/78] refactor: export indices to a global variable in squashedDictManager tests --- src/hints/squashedDictManager.test.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/hints/squashedDictManager.test.ts b/src/hints/squashedDictManager.test.ts index a9296369..78dbe324 100644 --- a/src/hints/squashedDictManager.test.ts +++ b/src/hints/squashedDictManager.test.ts @@ -5,6 +5,8 @@ import { EmptyKeys } from 'errors/squashedDictManager'; import { Felt } from 'primitives/felt'; import { SquashedDictManager } from './squashedDictManager'; +const INDICES = [new Felt(0n), new Felt(3n), new Felt(5n), new Felt(1n)]; + describe('SquashedDictManager', () => { test('should properly initialize a SquashedDictManager', () => { const squashedDictManager = new SquashedDictManager(); @@ -15,32 +17,29 @@ describe('SquashedDictManager', () => { test('should properly insert new index', () => { const squashedDictManager = new SquashedDictManager(); const key = new Felt(2n); - const indices = [new Felt(3n), new Felt(5n), new Felt(1n)]; squashedDictManager.keys.push(key); - indices.map((index) => squashedDictManager.insert(key, index)); - expect(squashedDictManager.lastIndices()).toEqual(indices); + INDICES.map((index) => squashedDictManager.insert(key, index)); + expect(squashedDictManager.lastIndices()).toEqual(INDICES); }); test('should properly get the last index', () => { const squashedDictManager = new SquashedDictManager(); const key = new Felt(2n); - const indices = [new Felt(3n), new Felt(5n), new Felt(1n)]; squashedDictManager.keys.push(key); - indices.map((index) => squashedDictManager.insert(key, index)); + INDICES.map((index) => squashedDictManager.insert(key, index)); expect(squashedDictManager.lastIndex()).toEqual( - indices[indices.length - 1] + INDICES[INDICES.length - 1] ); }); test('should properly pop the last index', () => { const squashedDictManager = new SquashedDictManager(); const key = new Felt(2n); - const indices = [new Felt(3n), new Felt(5n), new Felt(1n)]; squashedDictManager.keys.push(key); - indices.map((index) => squashedDictManager.insert(key, index)); - expect(squashedDictManager.popIndex()).toEqual(indices[indices.length - 1]); + INDICES.map((index) => squashedDictManager.insert(key, index)); + expect(squashedDictManager.popIndex()).toEqual(INDICES[INDICES.length - 1]); expect(squashedDictManager.lastIndices()).toEqual( - indices.slice(0, indices.length - 1) + INDICES.slice(0, INDICES.length - 1) ); }); From e47daabcdae69d92d4e0435254dabde54d8beb83 Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 24 Jul 2024 14:14:55 +0200 Subject: [PATCH 54/78] refactor: rename ResOp to ResOperand --- docs/howToImplementAHint.md | 8 ++-- src/errors/virtualMachine.ts | 8 ++-- src/hints/assertLeFindSmallArc.ts | 14 +++---- src/hints/dict/allocFelt252Dict.ts | 6 +-- src/hints/dict/felt252DictEntryInit.ts | 10 ++--- src/hints/dict/felt252DictEntryUpdate.ts | 10 ++--- src/hints/dict/getCurrentAccessIndex.ts | 6 +-- src/hints/dict/getSegmentArenaIndex.ts | 6 +-- src/hints/dict/initSquashData.ts | 14 +++---- src/hints/hintParamsSchema.test.ts | 8 ++-- src/hints/hintParamsSchema.ts | 10 ++--- src/hints/math/testLessThan.ts | 8 ++-- src/vm/virtualMachine.test.ts | 50 ++++++++++++++---------- src/vm/virtualMachine.ts | 32 +++++++-------- 14 files changed, 99 insertions(+), 91 deletions(-) diff --git a/docs/howToImplementAHint.md b/docs/howToImplementAHint.md index 52fc5c79..45c16bba 100644 --- a/docs/howToImplementAHint.md +++ b/docs/howToImplementAHint.md @@ -13,9 +13,9 @@ Find the signature of the hint in the GetSegmentArenaIndex { dict_end_ptr: ResOperand, dict_index: CellRef }, ``` -Here, `dict_end_ptr` is a `ResOp` while `dict_index` is a `CellRef`. +Here, `dict_end_ptr` is a `ResOperand` while `dict_index` is a `CellRef`. -The definitions of `Cellref` and `ResOp` can be found in `hintParamSchema.ts`. +The definitions of `Cellref` and `ResOperand` can be found in `hintParamSchema.ts`. Hint arguments can only be one of these two types. ## Parsing @@ -64,7 +64,7 @@ The GetSegmentArenaIndex hint can be found in a format similar to this one: export const getSegmentArenaIndexParser = z .object({ GetSegmentArenaIndex: z.object({ - dict_end_ptr: resOp, + dict_end_ptr: resOperand, dict_index: cellRef, }), }) @@ -104,7 +104,7 @@ The parameters of the function are the virtual machine, as the hint must interact with it, and the signature of the hint. So, in our case, the function signature would be -`export getSegmentArenaIndex(vm: VirtualMachine, dictEndPtr: ResOp, dictIndex: CellRef)` +`export getSegmentArenaIndex(vm: VirtualMachine, dictEndPtr: ResOperand, dictIndex: CellRef)` To implement the logic, refer yourself to its implementation in the [`cairo-vm`](https://github.com/lambdaclass/cairo-vm/blob/24c2349cc19832fd8c1552304fe0439765ed82c6/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs#L427-L444) diff --git a/src/errors/virtualMachine.ts b/src/errors/virtualMachine.ts index cc02c7f6..7a066cc9 100644 --- a/src/errors/virtualMachine.ts +++ b/src/errors/virtualMachine.ts @@ -1,4 +1,4 @@ -import { ResOp } from 'hints/hintParamsSchema'; +import { ResOperand } from 'hints/hintParamsSchema'; import { Felt } from 'primitives/felt'; import { Relocatable } from 'primitives/relocatable'; import { SegmentValue } from 'primitives/segmentValue'; @@ -60,10 +60,10 @@ export class UndefinedOp1 extends VirtualMachineError { } } -/** `resOp` is not of a valid type to extract buffer from it. */ +/** `resOperand` is not of a valid type to extract buffer from it. */ export class InvalidBufferResOp extends VirtualMachineError { - constructor(resOp: ResOp) { - super(`Cannot extract buffer from the given ResOp: ${resOp}`); + constructor(resOperand: ResOperand) { + super(`Cannot extract buffer from the given ResOperand: ${resOperand}`); } } diff --git a/src/hints/assertLeFindSmallArc.ts b/src/hints/assertLeFindSmallArc.ts index 4cebc066..e1ea4817 100644 --- a/src/hints/assertLeFindSmallArc.ts +++ b/src/hints/assertLeFindSmallArc.ts @@ -4,15 +4,15 @@ import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { ResOp, resOp } from 'hints/hintParamsSchema'; +import { ResOperand, resOperand } from 'hints/hintParamsSchema'; /** Zod object to parse AssertLeFindSmallArcs hint */ export const assertLeFindSmallArcsParser = z .object({ AssertLeFindSmallArcs: z.object({ - range_check_ptr: resOp, - a: resOp, - b: resOp, + range_check_ptr: resOperand, + a: resOperand, + b: resOperand, }), }) .transform(({ AssertLeFindSmallArcs: { range_check_ptr, a, b } }) => ({ @@ -42,9 +42,9 @@ export type Arc = { */ export const assertLeFindSmallArcs = ( vm: VirtualMachine, - rangeCheckPtr: ResOp, - a: ResOp, - b: ResOp + rangeCheckPtr: ResOperand, + a: ResOperand, + b: ResOperand ) => { const aVal = vm.getResOperandValue(a); const bVal = vm.getResOperandValue(b); diff --git a/src/hints/dict/allocFelt252Dict.ts b/src/hints/dict/allocFelt252Dict.ts index c85c75d7..62d3d2a0 100644 --- a/src/hints/dict/allocFelt252Dict.ts +++ b/src/hints/dict/allocFelt252Dict.ts @@ -7,11 +7,11 @@ import { isFelt, isRelocatable } from 'primitives/segmentValue'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { resOp, ResOp } from 'hints/hintParamsSchema'; +import { resOperand, ResOperand } from 'hints/hintParamsSchema'; /** Zod object to parse AllocFelt252Dict hint */ export const allocFelt252DictParser = z - .object({ AllocFelt252Dict: z.object({ segment_arena_ptr: resOp }) }) + .object({ AllocFelt252Dict: z.object({ segment_arena_ptr: resOperand }) }) .transform(({ AllocFelt252Dict: { segment_arena_ptr } }) => ({ type: HintName.AllocFelt252Dict, segmentArenaPtr: segment_arena_ptr, @@ -33,7 +33,7 @@ export type AllocFelt252Dict = z.infer; */ export const allocFelt252Dict = ( vm: VirtualMachine, - segmentArenaPtr: ResOp + segmentArenaPtr: ResOperand ) => { const arenaPtr = vm.getPointer(...vm.extractBuffer(segmentArenaPtr)); const dictNumber = vm.memory.get(arenaPtr.sub(2)); diff --git a/src/hints/dict/felt252DictEntryInit.ts b/src/hints/dict/felt252DictEntryInit.ts index 88cf1abd..684423dd 100644 --- a/src/hints/dict/felt252DictEntryInit.ts +++ b/src/hints/dict/felt252DictEntryInit.ts @@ -3,15 +3,15 @@ import { z } from 'zod'; import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; -import { resOp, ResOp } from 'hints/hintParamsSchema'; +import { resOperand, ResOperand } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; /** Zod object to parse Felt252DictEntryInit hint */ export const felt252DictEntryInitParser = z .object({ Felt252DictEntryInit: z.object({ - dict_ptr: resOp, - key: resOp, + dict_ptr: resOperand, + key: resOperand, }), }) .transform(({ Felt252DictEntryInit: { dict_ptr, key } }) => ({ @@ -39,8 +39,8 @@ export type Felt252DictEntryInit = z.infer; */ export const felt252DictEntryInit = ( vm: VirtualMachine, - dictPtr: ResOp, - key: ResOp + dictPtr: ResOperand, + key: ResOperand ) => { const address = vm.getPointer(...vm.extractBuffer(dictPtr)); const keyValue = vm.getResOperandValue(key).toString(); diff --git a/src/hints/dict/felt252DictEntryUpdate.ts b/src/hints/dict/felt252DictEntryUpdate.ts index 754a8d8b..bc9a90da 100644 --- a/src/hints/dict/felt252DictEntryUpdate.ts +++ b/src/hints/dict/felt252DictEntryUpdate.ts @@ -6,14 +6,14 @@ import { isFelt } from 'primitives/segmentValue'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { Deref, OpType, resOp, ResOp } from 'hints/hintParamsSchema'; +import { Deref, OpType, resOperand, ResOperand } from 'hints/hintParamsSchema'; /** Zod object to parse Felt252DictEntryUpdate hint */ export const felt252DictEntryUpdateParser = z .object({ Felt252DictEntryUpdate: z.object({ - dict_ptr: resOp, - value: resOp, + dict_ptr: resOperand, + value: resOperand, }), }) .transform(({ Felt252DictEntryUpdate: { dict_ptr, value } }) => ({ @@ -41,8 +41,8 @@ export type Felt252DictEntryUpdate = z.infer< */ export const felt252DictEntryUpdate = ( vm: VirtualMachine, - dictPtr: ResOp, - value: ResOp + dictPtr: ResOperand, + value: ResOperand ) => { const address = vm.getPointer(...vm.extractBuffer(dictPtr)); const key = vm.memory.get(address.sub(3)); diff --git a/src/hints/dict/getCurrentAccessIndex.ts b/src/hints/dict/getCurrentAccessIndex.ts index a08ddc9e..5fea3099 100644 --- a/src/hints/dict/getCurrentAccessIndex.ts +++ b/src/hints/dict/getCurrentAccessIndex.ts @@ -2,12 +2,12 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; -import { resOp, ResOp } from 'hints/hintParamsSchema'; +import { resOperand, ResOperand } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; /** Zod object to parse GetCurrentAccessIndex hint */ export const getCurrentAccessIndexParser = z - .object({ GetCurrentAccessIndex: z.object({ range_check_ptr: resOp }) }) + .object({ GetCurrentAccessIndex: z.object({ range_check_ptr: resOperand }) }) .transform(({ GetCurrentAccessIndex: { range_check_ptr } }) => ({ type: HintName.GetCurrentAccessIndex, rangeCheckPtr: range_check_ptr, @@ -24,7 +24,7 @@ export type GetCurrentAccessIndex = z.infer; */ export const getCurrentAccessIndex = ( vm: VirtualMachine, - rangeCheckPtr: ResOp + rangeCheckPtr: ResOperand ) => { vm.memory.assertEq( vm.getPointer(...vm.extractBuffer(rangeCheckPtr)), diff --git a/src/hints/dict/getSegmentArenaIndex.ts b/src/hints/dict/getSegmentArenaIndex.ts index d8f0a442..613941dc 100644 --- a/src/hints/dict/getSegmentArenaIndex.ts +++ b/src/hints/dict/getSegmentArenaIndex.ts @@ -3,13 +3,13 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { resOp, ResOp, cellRef, CellRef } from 'hints/hintParamsSchema'; +import { resOperand, ResOperand, cellRef, CellRef } from 'hints/hintParamsSchema'; /** Zod object to parse GetSegmentArenaIndex hint */ export const getSegmentArenaIndexParser = z .object({ GetSegmentArenaIndex: z.object({ - dict_end_ptr: resOp, + dict_end_ptr: resOperand, dict_index: cellRef, }), }) @@ -37,7 +37,7 @@ export type GetSegmentArenaIndex = z.infer; */ export const getSegmentArenaIndex = ( vm: VirtualMachine, - dictEndPtr: ResOp, + dictEndPtr: ResOperand, dictIndex: CellRef ) => { const address = vm.getPointer(...vm.extractBuffer(dictEndPtr)); diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index df2eef58..1a1334c8 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -7,15 +7,15 @@ import { isFelt } from 'primitives/segmentValue'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { CellRef, cellRef, resOp, ResOp } from 'hints/hintParamsSchema'; +import { CellRef, cellRef, resOperand, ResOperand } from 'hints/hintParamsSchema'; /** Zod object to parse InitSquashData hint */ export const initSquashDataParser = z .object({ InitSquashData: z.object({ - dict_accesses: resOp, - ptr_diff: resOp, - n_accesses: resOp, + dict_accesses: resOperand, + ptr_diff: resOperand, + n_accesses: resOperand, big_keys: cellRef, first_key: cellRef, }), @@ -56,9 +56,9 @@ export type InitSquashData = z.infer; */ export const initSquashData = ( vm: VirtualMachine, - dictAccesses: ResOp, - ptrDiff: ResOp, - nAccesses: ResOp, + dictAccesses: ResOperand, + ptrDiff: ResOperand, + nAccesses: ResOperand, bigKeys: CellRef, firstKey: CellRef ) => { diff --git a/src/hints/hintParamsSchema.test.ts b/src/hints/hintParamsSchema.test.ts index 4530a114..3cad0aec 100644 --- a/src/hints/hintParamsSchema.test.ts +++ b/src/hints/hintParamsSchema.test.ts @@ -7,9 +7,9 @@ import { CellRef, OpType, Operation, - ResOp, + ResOperand, cellRef, - resOp, + resOperand, } from './hintParamsSchema'; describe('hintParamsSchema', () => { @@ -128,7 +128,7 @@ describe('hintParamsSchema', () => { }, }, ], - ])('should properly parse ResOp', (value, expected: ResOp) => { - expect(resOp.parse(value)).toEqual(expected); + ])('should properly parse ResOperand', (value, expected: ResOperand) => { + expect(resOperand.parse(value)).toEqual(expected); }); }); diff --git a/src/hints/hintParamsSchema.ts b/src/hints/hintParamsSchema.ts index aca1044a..c612543b 100644 --- a/src/hints/hintParamsSchema.ts +++ b/src/hints/hintParamsSchema.ts @@ -4,7 +4,7 @@ import { Felt } from 'primitives/felt'; import { Register } from 'vm/instruction'; /** - * Types to distinguish ResOp pattern + * Types to distinguish ResOperand pattern * * Generic patterns: * - Deref: `[register + offset]` @@ -87,8 +87,8 @@ const binOp = z b: object.BinOp.b, })); -/** Zod object to parse a ResOp */ -export const resOp = z.union([deref, doubleDeref, immediate, binOp]); +/** Zod object to parse a ResOperand */ +export const resOperand = z.union([deref, doubleDeref, immediate, binOp]); /** * A CellRef is an object defining a register and an offset @@ -128,5 +128,5 @@ export type Immediate = z.infer; */ export type BinOp = z.infer; -/** A ResOp is either a Deref, DoubleDeref, Immediate or BinOp */ -export type ResOp = z.infer; +/** A ResOperand is either a Deref, DoubleDeref, Immediate or BinOp */ +export type ResOperand = z.infer; diff --git a/src/hints/math/testLessThan.ts b/src/hints/math/testLessThan.ts index 1174e956..e7d7ca84 100644 --- a/src/hints/math/testLessThan.ts +++ b/src/hints/math/testLessThan.ts @@ -4,12 +4,12 @@ import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { cellRef, resOp, CellRef, ResOp } from 'hints/hintParamsSchema'; +import { cellRef, resOperand, CellRef, ResOperand } from 'hints/hintParamsSchema'; /** Zod object to parse TestLessThan hint */ export const testLessThanParser = z .object({ - TestLessThan: z.object({ lhs: resOp, rhs: resOp, dst: cellRef }), + TestLessThan: z.object({ lhs: resOperand, rhs: resOperand, dst: cellRef }), }) .transform(({ TestLessThan: { lhs, rhs, dst } }) => ({ type: HintName.TestLessThan, @@ -35,8 +35,8 @@ export type TestLessThan = z.infer; */ export const testLessThan = ( vm: VirtualMachine, - lhs: ResOp, - rhs: ResOp, + lhs: ResOperand, + rhs: ResOperand, dst: CellRef ) => { const lhsValue = vm.getResOperandValue(lhs); diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index 51c6cc92..94d1c60b 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -21,7 +21,7 @@ import { Op1Src, } from './instruction'; import { Dictionnary, VirtualMachine } from './virtualMachine'; -import { CellRef, Operation, OpType, ResOp } from 'hints/hintParamsSchema'; +import { CellRef, Operation, OpType, ResOperand } from 'hints/hintParamsSchema'; const instructions = { InvalidAssertEq: new Instruction( @@ -610,10 +610,15 @@ describe('VirtualMachine', () => { }, [{ register: Register.Fp, offset: 2 }, new Felt(5n)], ], - ])('should properly extract a buffer', (resOp: ResOp, expected) => { - const vm = new VirtualMachine(); - expect(vm.extractBuffer(resOp)).toEqual(expected as [CellRef, Felt]); - }); + ])( + 'should properly extract a buffer', + (resOperand: ResOperand, expected) => { + const vm = new VirtualMachine(); + expect(vm.extractBuffer(resOperand)).toEqual( + expected as [CellRef, Felt] + ); + } + ); test.each([ { @@ -646,11 +651,11 @@ describe('VirtualMachine', () => { value: new Felt(4n), }, ])( - 'should throw when extracting a buffer with the wrong resOp', - (resOp: ResOp) => { + 'should throw when extracting a buffer with the wrong resOperand', + (resOperand: ResOperand) => { const vm = new VirtualMachine(); - expect(() => vm.extractBuffer(resOp)).toThrow( - new InvalidBufferResOp(resOp) + expect(() => vm.extractBuffer(resOperand)).toThrow( + new InvalidBufferResOp(resOperand) ); } ); @@ -720,18 +725,21 @@ describe('VirtualMachine', () => { }, new Felt(21n), ], - ])('should properly read ResOp', (resOp: ResOp, expected: Felt) => { - const vm = new VirtualMachine(); - vm.memory.addSegment(); - vm.memory.addSegment(); - const value0 = new Felt(3n); - const value1 = new Felt(7n); - const address = new Relocatable(1, 0); - vm.memory.assertEq(vm.ap, value0); - vm.memory.assertEq(vm.ap.add(1), value1); - vm.memory.assertEq(vm.ap.add(2), address); - expect(vm.getResOperandValue(resOp)).toEqual(expected); - }); + ])( + 'should properly read ResOperand', + (resOperand: ResOperand, expected: Felt) => { + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + const value0 = new Felt(3n); + const value1 = new Felt(7n); + const address = new Relocatable(1, 0); + vm.memory.assertEq(vm.ap, value0); + vm.memory.assertEq(vm.ap.add(1), value1); + vm.memory.assertEq(vm.ap.add(2), address); + expect(vm.getResOperandValue(resOperand)).toEqual(expected); + } + ); }); }); diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 5a17ffb0..90bbadc8 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -27,7 +27,7 @@ import { Immediate, Operation, OpType, - ResOp, + ResOperand, } from 'hints/hintParamsSchema'; import { Hint } from 'hints/hintSchema'; import { ScopeManager } from 'hints/scopeManager'; @@ -550,7 +550,7 @@ export class VirtualMachine { } /** - * Return the value defined by `resOp` + * Return the value defined by `resOperand` * * Generic patterns: * - Deref: `[register + offset]` @@ -563,23 +563,23 @@ export class VirtualMachine { * * NOTE: used in Cairo hints */ - getResOperandValue(resOp: ResOp): Felt { - switch (resOp.type) { + getResOperandValue(resOperand: ResOperand): Felt { + switch (resOperand.type) { case OpType.Deref: - return this.getFelt((resOp as Deref).cell); + return this.getFelt((resOperand as Deref).cell); case OpType.DoubleDeref: - const dDeref = resOp as DoubleDeref; + const dDeref = resOperand as DoubleDeref; const deref = this.getRelocatable(dDeref.cell); const value = this.memory.get(deref.add(dDeref.offset)); if (!value || !isFelt(value)) throw new ExpectedFelt(value); return value; case OpType.Immediate: - return (resOp as Immediate).value; + return (resOperand as Immediate).value; case OpType.BinOp: - const binOp = resOp as BinOp; + const binOp = resOperand as BinOp; const a = this.getFelt(binOp.a); let b: Felt | undefined = undefined; @@ -607,25 +607,25 @@ export class VirtualMachine { } /** - * Return the address defined at `resOp`. + * Return the address defined at `resOperand`. * - * This method assume that resOp points to a Relocatable. + * This method assume that resOperand points to a Relocatable. * * Only Deref and BinOp with Immediate value are valid for extracting a buffer. * * NOTE: Used in Cairo hints. */ - extractBuffer(resOp: ResOp): [CellRef, Felt] { - switch (resOp.type) { + extractBuffer(resOperand: ResOperand): [CellRef, Felt] { + switch (resOperand.type) { case OpType.Deref: - return [(resOp as Deref).cell, new Felt(0n)]; + return [(resOperand as Deref).cell, new Felt(0n)]; case OpType.BinOp: - const binOp = resOp as BinOp; + const binOp = resOperand as BinOp; if (binOp.b.type !== OpType.Immediate) - throw new InvalidBufferResOp(resOp); + throw new InvalidBufferResOp(resOperand); return [binOp.a, (binOp.b as Immediate).value]; default: - throw new InvalidBufferResOp(resOp); + throw new InvalidBufferResOp(resOperand); } } From 77e2b053099010475697e6863427bae4fe03838f Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 24 Jul 2024 14:25:26 +0200 Subject: [PATCH 55/78] chore: trunk fmt --- docs/howToImplementAHint.md | 4 ++-- src/hints/dict/getSegmentArenaIndex.ts | 7 ++++++- src/hints/dict/initSquashData.ts | 7 ++++++- src/hints/math/testLessThan.ts | 7 ++++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/howToImplementAHint.md b/docs/howToImplementAHint.md index 45c16bba..0d5734a5 100644 --- a/docs/howToImplementAHint.md +++ b/docs/howToImplementAHint.md @@ -15,8 +15,8 @@ GetSegmentArenaIndex { dict_end_ptr: ResOperand, dict_index: CellRef }, Here, `dict_end_ptr` is a `ResOperand` while `dict_index` is a `CellRef`. -The definitions of `Cellref` and `ResOperand` can be found in `hintParamSchema.ts`. -Hint arguments can only be one of these two types. +The definitions of `Cellref` and `ResOperand` can be found in +`hintParamSchema.ts`. Hint arguments can only be one of these two types. ## Parsing diff --git a/src/hints/dict/getSegmentArenaIndex.ts b/src/hints/dict/getSegmentArenaIndex.ts index 613941dc..af89d585 100644 --- a/src/hints/dict/getSegmentArenaIndex.ts +++ b/src/hints/dict/getSegmentArenaIndex.ts @@ -3,7 +3,12 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { resOperand, ResOperand, cellRef, CellRef } from 'hints/hintParamsSchema'; +import { + resOperand, + ResOperand, + cellRef, + CellRef, +} from 'hints/hintParamsSchema'; /** Zod object to parse GetSegmentArenaIndex hint */ export const getSegmentArenaIndexParser = z diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index 1a1334c8..e4f52557 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -7,7 +7,12 @@ import { isFelt } from 'primitives/segmentValue'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { CellRef, cellRef, resOperand, ResOperand } from 'hints/hintParamsSchema'; +import { + CellRef, + cellRef, + resOperand, + ResOperand, +} from 'hints/hintParamsSchema'; /** Zod object to parse InitSquashData hint */ export const initSquashDataParser = z diff --git a/src/hints/math/testLessThan.ts b/src/hints/math/testLessThan.ts index e7d7ca84..d48c0a55 100644 --- a/src/hints/math/testLessThan.ts +++ b/src/hints/math/testLessThan.ts @@ -4,7 +4,12 @@ import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; -import { cellRef, resOperand, CellRef, ResOperand } from 'hints/hintParamsSchema'; +import { + cellRef, + resOperand, + CellRef, + ResOperand, +} from 'hints/hintParamsSchema'; /** Zod object to parse TestLessThan hint */ export const testLessThanParser = z From 145f011434646cd056dbb569939f378884088eef Mon Sep 17 00:00:00 2001 From: malatrax Date: Wed, 24 Jul 2024 14:27:21 +0200 Subject: [PATCH 56/78] feat: enable trunk-fmt-pre-commit --- .trunk/trunk.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 391fb8c4..b93c2fe9 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -45,6 +45,5 @@ actions: disabled: - trunk-announce - trunk-check-pre-push - - trunk-fmt-pre-commit enabled: - trunk-upgrade-available From c26370989d5383506caf67c8577fc43bfc3a4320 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Mon, 29 Jul 2024 14:32:13 +0200 Subject: [PATCH 57/78] chore: rephase JSDoc SquashedDictManager error --- src/errors/squashedDictManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/errors/squashedDictManager.ts b/src/errors/squashedDictManager.ts index db2b671b..9c7dd2e3 100644 --- a/src/errors/squashedDictManager.ts +++ b/src/errors/squashedDictManager.ts @@ -2,7 +2,7 @@ import { Felt } from 'primitives/felt'; class SquashedDictManagerError extends Error {} -/** There is no keys in the squashedDictionnaryManager */ +/** There is no key in the squashed dictionnary manager */ export class EmptyKeys extends SquashedDictManagerError { constructor() { super('There is no keys left in the squashed dictionnary manager.'); From d629f3f16fe94f1a8bc34b81c5111bfe2ce4afee Mon Sep 17 00:00:00 2001 From: Malatrax Date: Mon, 29 Jul 2024 20:05:24 +0200 Subject: [PATCH 58/78] chore: remove unused methods --- src/errors/virtualMachine.ts | 10 ---------- src/vm/virtualMachine.test.ts | 19 ------------------ src/vm/virtualMachine.ts | 37 ++++++++++++++++++++--------------- 3 files changed, 21 insertions(+), 45 deletions(-) diff --git a/src/errors/virtualMachine.ts b/src/errors/virtualMachine.ts index 7a066cc9..251a1d9f 100644 --- a/src/errors/virtualMachine.ts +++ b/src/errors/virtualMachine.ts @@ -1,5 +1,4 @@ import { ResOperand } from 'hints/hintParamsSchema'; -import { Felt } from 'primitives/felt'; import { Relocatable } from 'primitives/relocatable'; import { SegmentValue } from 'primitives/segmentValue'; @@ -73,12 +72,3 @@ export class DictNotFound extends VirtualMachineError { super(`Cannot found any Dictionnary at address ${address.toString()}`); } } - -/** Cannot find value at `key` in Dictionnary at `address */ -export class DictValueNotFound extends VirtualMachineError { - constructor(address: Relocatable, key: Felt) { - super( - `Cannot found value at ${key.toString()} from Dictionnary at address ${address.toString()}` - ); - } -} diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index 94d1c60b..97b8eed2 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -3,7 +3,6 @@ import { test, expect, describe, spyOn } from 'bun:test'; import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; import { DictNotFound, - DictValueNotFound, InvalidBufferResOp, UnusedRes, } from 'errors/virtualMachine'; @@ -756,28 +755,10 @@ describe('VirtualMachine', () => { expect(vm.getDict(address)).toEqual(new Dictionnary(new Felt(0n))); }); - test('should properly set and get value of a dictionnary', () => { - const vm = new VirtualMachine(); - const address = vm.newDict(); - const key = new Felt(12n); - const value = new Felt(5n); - vm.setDictValue(address, key, value); - expect(vm.getDictValue(address, key)).toEqual(value); - }); - test('should throw DictNotFound when accessing a non-existing dictionnary', () => { const vm = new VirtualMachine(); const address = new Relocatable(2, 3); expect(() => vm.getDict(address)).toThrow(new DictNotFound(address)); }); - - test('should throw DictValueNotFound when accessing a non-existing key in a dictionnary', () => { - const vm = new VirtualMachine(); - const address = vm.newDict(); - const key = new Felt(0n); - expect(() => vm.getDictValue(address, key)).toThrow( - new DictValueNotFound(address, key) - ); - }); }); }); diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 90bbadc8..ee3bff5a 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -10,7 +10,6 @@ import { InvalidCallOp0Value, UndefinedOp1, DictNotFound, - DictValueNotFound, InvalidBufferResOp, } from 'errors/virtualMachine'; import { InvalidCellRefRegister, UnknownHint } from 'errors/hints'; @@ -32,7 +31,7 @@ import { import { Hint } from 'hints/hintSchema'; import { ScopeManager } from 'hints/scopeManager'; import { SquashedDictManager } from '../hints/squashedDictManager'; -import { handlers } from 'hints/hintHandler'; +import { handlers, HintHandler } from 'hints/hintHandler'; import { ApUpdate, @@ -82,8 +81,7 @@ export class VirtualMachine { relocatedMemory: RelocatedMemory[]; relocatedTrace: RelocatedTraceEntry[]; - /** Maps a hint to its implementation */ - private handlers = handlers; + private handlers: HintHandler = handlers; constructor() { this.currentStep = 0n; @@ -543,6 +541,13 @@ export class VirtualMachine { return value; } + /** + * Return the memory value at the address defined by `cell` + * + * Throw if the value is `undefined` + * + * NOTE: used in Cairo hints + */ getSegmentValue(cell: CellRef): SegmentValue { const value = this.memory.get(this.cellRefToRelocatable(cell)); if (!value) throw new UndefinedSegmentValue(); @@ -629,6 +634,11 @@ export class VirtualMachine { } } + /** + * Creates a new dictionnary. + * + * NOTE: used in Cairo hints + */ newDict(): Relocatable { const dictAddr = this.memory.addSegment(); this.dictManager.set( @@ -638,21 +648,16 @@ export class VirtualMachine { return dictAddr; } + /** + * Return the dictionnary at `address` + * + * Throw if dictionnary was not found + * + * NOTE: used in Cairo hints + */ getDict(address: Relocatable): Dictionnary { const dict = this.dictManager.get(address.segmentId); if (!dict) throw new DictNotFound(address); return dict; } - - getDictValue(address: Relocatable, key: Felt): SegmentValue { - const dict = this.getDict(address); - const value = dict.get(key.toString()); - if (!value) throw new DictValueNotFound(address, key); - return value; - } - - setDictValue(address: Relocatable, key: Felt, value: SegmentValue) { - const dict = this.getDict(address); - dict.set(key.toString(), value); - } } From 7c5a04d675888087ae327709686e526c8c2ebb60 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Mon, 29 Jul 2024 20:06:23 +0200 Subject: [PATCH 59/78] doc: fix JSDoc typos --- src/errors/squashedDictManager.ts | 2 +- src/hints/assertLeFindSmallArc.ts | 5 ++++- src/hints/assertLeIsFirstArcExcluded.ts | 4 +++- src/hints/assertLeIsSecondArcExcluded.ts | 4 +++- src/hints/dict/felt252DictEntryInit.ts | 2 +- src/hints/dict/felt252DictEntryUpdate.ts | 2 +- src/hints/dict/getCurrentAccessDelta.ts | 3 +-- src/hints/dict/getCurrentAccessIndex.ts | 5 +---- src/hints/dict/getNextDictKey.ts | 4 +--- src/hints/dict/getSegmentArenaIndex.ts | 2 +- src/hints/dict/shouldContinueSquashLoop.ts | 10 +++------- src/hints/dict/shouldSkipSquashLoop.ts | 8 ++------ src/hints/hintHandler.ts | 9 +++++++-- src/hints/squashedDictManager.ts | 2 +- 14 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/errors/squashedDictManager.ts b/src/errors/squashedDictManager.ts index 9c7dd2e3..7738b119 100644 --- a/src/errors/squashedDictManager.ts +++ b/src/errors/squashedDictManager.ts @@ -2,7 +2,7 @@ import { Felt } from 'primitives/felt'; class SquashedDictManagerError extends Error {} -/** There is no key in the squashed dictionnary manager */ +/** There is no keys left in the squashed dictionnary manager */ export class EmptyKeys extends SquashedDictManagerError { constructor() { super('There is no keys left in the squashed dictionnary manager.'); diff --git a/src/hints/assertLeFindSmallArc.ts b/src/hints/assertLeFindSmallArc.ts index e1ea4817..fe325d64 100644 --- a/src/hints/assertLeFindSmallArc.ts +++ b/src/hints/assertLeFindSmallArc.ts @@ -24,6 +24,9 @@ export const assertLeFindSmallArcsParser = z /** * AssertLeFindSmallArcs hint + * + * Find the two small arcs from [(0, a), (a, b), (b, PRIME)] and + * assert them to the range check segment. */ export type AssertLeFindSmallArcs = z.infer; @@ -33,7 +36,7 @@ export type Arc = { }; /** - * Compute the three arcs `a`, `b - a` and `PRIME - 1 - b` + * Compute the three arcs [(0, a), (a, b), (b, PRIME)] * * Set the biggest arc to the scope variable `excluded_arc` * diff --git a/src/hints/assertLeIsFirstArcExcluded.ts b/src/hints/assertLeIsFirstArcExcluded.ts index fe0f9bf4..5986765c 100644 --- a/src/hints/assertLeIsFirstArcExcluded.ts +++ b/src/hints/assertLeIsFirstArcExcluded.ts @@ -19,13 +19,15 @@ export const assertLeIsFirstArcExcludedParser = z /** * AssertLeIsFirstArcExcluded hint + * + * Assert if the arc (0, a) was excluded. */ export type AssertLeIsFirstArcExcluded = z.infer< typeof assertLeIsFirstArcExcludedParser >; /** - * Check whether the first arc from `AssertLeFindSmallArcs` is excluded. + * Assert if the arc (0, a) from `AssertLeFindSmallArcs` was excluded. * * Read the value in scope at `excluded_arc` */ diff --git a/src/hints/assertLeIsSecondArcExcluded.ts b/src/hints/assertLeIsSecondArcExcluded.ts index bb37ae56..c8df62a2 100644 --- a/src/hints/assertLeIsSecondArcExcluded.ts +++ b/src/hints/assertLeIsSecondArcExcluded.ts @@ -19,13 +19,15 @@ export const assertLeIsSecondArcExcludedParser = z /** * AssertLeIsSecondArcExcluded hint + * + * Assert if the arc (a, b) was excluded. */ export type AssertLeIsSecondArcExcluded = z.infer< typeof assertLeIsSecondArcExcludedParser >; /** - * Check whether the second arc from `AssertLeFindSmallArcs` is excluded. + * Assert if the arc (a, b) from `AssertLeFindSmallArcs` was excluded. * * Read the value in scope at `excluded_arc` */ diff --git a/src/hints/dict/felt252DictEntryInit.ts b/src/hints/dict/felt252DictEntryInit.ts index 684423dd..f82b669d 100644 --- a/src/hints/dict/felt252DictEntryInit.ts +++ b/src/hints/dict/felt252DictEntryInit.ts @@ -34,7 +34,7 @@ export type Felt252DictEntryInit = z.infer; * Read the current value at `key` if any, * Initialize `dict[key]` to 0 if undefined. * - * Assert the value at key to the `dict` segment. + * Assert the value at `key` to the `dict` segment. * */ export const felt252DictEntryInit = ( diff --git a/src/hints/dict/felt252DictEntryUpdate.ts b/src/hints/dict/felt252DictEntryUpdate.ts index bc9a90da..71186474 100644 --- a/src/hints/dict/felt252DictEntryUpdate.ts +++ b/src/hints/dict/felt252DictEntryUpdate.ts @@ -25,7 +25,7 @@ export const felt252DictEntryUpdateParser = z /** * Felt252DictEntryUpdate hint * - * Set a value in a dict: `dict[key] = value` + * Update an existing dict entry `key` to `value`: `dict[key] = value` */ export type Felt252DictEntryUpdate = z.infer< typeof felt252DictEntryUpdateParser diff --git a/src/hints/dict/getCurrentAccessDelta.ts b/src/hints/dict/getCurrentAccessDelta.ts index 20d564b1..f7691f7c 100644 --- a/src/hints/dict/getCurrentAccessDelta.ts +++ b/src/hints/dict/getCurrentAccessDelta.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; -import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; @@ -17,7 +16,7 @@ export const getCurrentAccessDeltaParser = z /** * GetCurrentAccessDelta hint * - * Assert to memory the difference between the current and previous indices. + * Assert to memory the difference between the current index to the next one. * */ export type GetCurrentAccessDelta = z.infer; diff --git a/src/hints/dict/getCurrentAccessIndex.ts b/src/hints/dict/getCurrentAccessIndex.ts index 5fea3099..d8771ef3 100644 --- a/src/hints/dict/getCurrentAccessIndex.ts +++ b/src/hints/dict/getCurrentAccessIndex.ts @@ -13,10 +13,7 @@ export const getCurrentAccessIndexParser = z rangeCheckPtr: range_check_ptr, })); -/** - * GetCurrentAccessIndex hint - * - */ +/** GetCurrentAccessIndex hint */ export type GetCurrentAccessIndex = z.infer; /** diff --git a/src/hints/dict/getNextDictKey.ts b/src/hints/dict/getNextDictKey.ts index a5ef85a0..8b4b5486 100644 --- a/src/hints/dict/getNextDictKey.ts +++ b/src/hints/dict/getNextDictKey.ts @@ -13,9 +13,7 @@ export const getNextDictKeyParser = z nextKey: next_key, })); -/** - * GetNextDictKey hint - */ +/** GetNextDictKey hint */ export type GetNextDictKey = z.infer; /** diff --git a/src/hints/dict/getSegmentArenaIndex.ts b/src/hints/dict/getSegmentArenaIndex.ts index af89d585..121527b1 100644 --- a/src/hints/dict/getSegmentArenaIndex.ts +++ b/src/hints/dict/getSegmentArenaIndex.ts @@ -27,7 +27,7 @@ export const getSegmentArenaIndexParser = z /** * GetSegmentArenaIndex hint * - * Assert the index of the dictionnary to its segment. + * Retrieve the index of the given dict in the info segment. * Used when finalizing the dictionnaries. */ export type GetSegmentArenaIndex = z.infer; diff --git a/src/hints/dict/shouldContinueSquashLoop.ts b/src/hints/dict/shouldContinueSquashLoop.ts index 6597acf8..5adc2191 100644 --- a/src/hints/dict/shouldContinueSquashLoop.ts +++ b/src/hints/dict/shouldContinueSquashLoop.ts @@ -14,19 +14,15 @@ export const shouldContinueSquashLoopParser = z shouldContinue: should_continue, })); -/** - * ShouldContinueSquashLoop hint - * - * Check whether the squash loop should be skipped. - */ +/** ShouldContinueSquashLoop hint */ export type ShouldContinueSquashLoop = z.infer< typeof shouldContinueSquashLoopParser >; /** - * Check whether the squash loop should be skipped. + * Assert whether the squash loop should be continued. * - * If there is still indices to be squashed, the loop is skipped. + * If there is still indices to be squashed, the loop continue. */ export const shouldContinueSquashLoop = ( vm: VirtualMachine, diff --git a/src/hints/dict/shouldSkipSquashLoop.ts b/src/hints/dict/shouldSkipSquashLoop.ts index 1e7d954d..2c5d044b 100644 --- a/src/hints/dict/shouldSkipSquashLoop.ts +++ b/src/hints/dict/shouldSkipSquashLoop.ts @@ -14,17 +14,13 @@ export const shouldSkipSquashLoopParser = z shouldSkipLoop: should_skip_loop, })); -/** - * ShouldSkipSquashLoop hint - * - * Check whether the squash loop should be skipped. - */ +/** ShouldSkipSquashLoop hint */ export type ShouldSkipSquashLoop = z.infer; /** * Check whether the squash loop should be skipped. * - * If there is still indices to be squashed, the loop is skipped. + * If there is no more indices to be squashed, the loop is skipped. */ export const shouldSkipSquashLoop = ( vm: VirtualMachine, diff --git a/src/hints/hintHandler.ts b/src/hints/hintHandler.ts index 58b8926d..020aeeab 100644 --- a/src/hints/hintHandler.ts +++ b/src/hints/hintHandler.ts @@ -49,10 +49,15 @@ import { } from './dict/shouldSkipSquashLoop'; import { TestLessThan, testLessThan } from './math/testLessThan'; -export const handlers: Record< +/** + * Map hint names to the function executing their logic. + */ +export type HintHandler = Record< HintName, (vm: VirtualMachine, hint: Hint) => void -> = { +>; + +export const handlers: HintHandler = { [HintName.AllocFelt252Dict]: (vm, hint) => { const h = hint as AllocFelt252Dict; allocFelt252Dict(vm, h.segmentArenaPtr); diff --git a/src/hints/squashedDictManager.ts b/src/hints/squashedDictManager.ts index 7d0d27aa..69c29cd1 100644 --- a/src/hints/squashedDictManager.ts +++ b/src/hints/squashedDictManager.ts @@ -12,7 +12,7 @@ import { Felt } from 'primitives/felt'; export class SquashedDictManager { /** Maps the key of a dictionnary to its taken values accross the run. */ public keyToIndices: Map; - /** An array containing the keys that still needs to be squashed. */ + /** An array containing the keys that still need to be squashed. */ public keys: Felt[]; constructor() { From 458b7dee8157ffe3934fbea2c61edfa638a147ca Mon Sep 17 00:00:00 2001 From: Malatrax <71888134+zmalatrax@users.noreply.github.com> Date: Tue, 30 Jul 2024 14:00:48 +0200 Subject: [PATCH 60/78] chore: typo Co-authored-by: Mathieu <60658558+enitrat@users.noreply.github.com> --- src/errors/virtualMachine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/errors/virtualMachine.ts b/src/errors/virtualMachine.ts index 251a1d9f..db60df9a 100644 --- a/src/errors/virtualMachine.ts +++ b/src/errors/virtualMachine.ts @@ -66,7 +66,7 @@ export class InvalidBufferResOp extends VirtualMachineError { } } -/** Cannot find Dictionnary at `address */ +/** Cannot find Dictionnary at `address` */ export class DictNotFound extends VirtualMachineError { constructor(address: Relocatable) { super(`Cannot found any Dictionnary at address ${address.toString()}`); From f28151cb0850b040a948eb9b624a0dd160348e72 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 13:22:11 +0200 Subject: [PATCH 61/78] feat: overload Felt mul signature with multiplication by number --- src/primitives/felt.test.ts | 6 ++++++ src/primitives/felt.ts | 14 +++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/primitives/felt.test.ts b/src/primitives/felt.test.ts index 1c24c41f..c58ca9e2 100644 --- a/src/primitives/felt.test.ts +++ b/src/primitives/felt.test.ts @@ -120,6 +120,12 @@ describe('Felt', () => { ); expect(result.eq(expected)).toBeTrue(); }); + test('should multiply a felt and a number properly', () => { + const a = new Felt(10n); + const b = 20; + const expected = new Felt(200n); + expect(a.mul(b)).toEqual(expected); + }); }); describe('div', () => { diff --git a/src/primitives/felt.ts b/src/primitives/felt.ts index 43aaac9c..d6b5da44 100644 --- a/src/primitives/felt.ts +++ b/src/primitives/felt.ts @@ -62,12 +62,20 @@ export class Felt { return new Felt(this.inner - BigInt(other)); } - mul(other: SegmentValue): Felt { - if (!isFelt(other)) { + mul(other: Felt): Felt; + mul(other: number): Felt; + mul(other: Relocatable): never; + mul(other: SegmentValue): SegmentValue; + mul(other: SegmentValue | number): SegmentValue { + if (isRelocatable(other)) { throw new ExpectedFelt(other); } - return new Felt(this.inner * other.inner); + if (isFelt(other)) { + return new Felt(this.inner * other.inner); + } + + return new Felt(this.inner * BigInt(other)); } neg(): Felt { From be29cf200cfce650fa632c2622c6e4809ef47fdf Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 13:45:05 +0200 Subject: [PATCH 62/78] refactor: use named constant DICT_ACCESS_SIZE --- src/hints/dict/allocFelt252Dict.ts | 4 ++-- src/hints/dict/felt252DictEntryUpdate.ts | 4 ++-- src/hints/dict/initSquashData.ts | 12 ++++++++---- src/vm/virtualMachine.ts | 2 ++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/hints/dict/allocFelt252Dict.ts b/src/hints/dict/allocFelt252Dict.ts index 62d3d2a0..7f7f0794 100644 --- a/src/hints/dict/allocFelt252Dict.ts +++ b/src/hints/dict/allocFelt252Dict.ts @@ -4,7 +4,7 @@ import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; import { Felt } from 'primitives/felt'; import { isFelt, isRelocatable } from 'primitives/segmentValue'; -import { VirtualMachine } from 'vm/virtualMachine'; +import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; import { resOperand, ResOperand } from 'hints/hintParamsSchema'; @@ -44,5 +44,5 @@ export const allocFelt252Dict = ( throw new ExpectedRelocatable(infoPtr); const newDict = vm.newDict(); - vm.memory.assertEq(infoPtr.add(dictNumber.mul(new Felt(3n))), newDict); + vm.memory.assertEq(infoPtr.add(dictNumber.mul(DICT_ACCESS_SIZE)), newDict); }; diff --git a/src/hints/dict/felt252DictEntryUpdate.ts b/src/hints/dict/felt252DictEntryUpdate.ts index 71186474..29096421 100644 --- a/src/hints/dict/felt252DictEntryUpdate.ts +++ b/src/hints/dict/felt252DictEntryUpdate.ts @@ -3,7 +3,7 @@ import { z } from 'zod'; import { ExpectedFelt } from 'errors/primitives'; import { isFelt } from 'primitives/segmentValue'; -import { VirtualMachine } from 'vm/virtualMachine'; +import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; import { Deref, OpType, resOperand, ResOperand } from 'hints/hintParamsSchema'; @@ -45,7 +45,7 @@ export const felt252DictEntryUpdate = ( value: ResOperand ) => { const address = vm.getPointer(...vm.extractBuffer(dictPtr)); - const key = vm.memory.get(address.sub(3)); + const key = vm.memory.get(address.sub(DICT_ACCESS_SIZE)); if (!key || !isFelt(key)) throw new ExpectedFelt(key); const val = value.type === OpType.Deref diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index e4f52557..f7098cd3 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -4,7 +4,7 @@ import { ExpectedFelt } from 'errors/primitives'; import { Felt } from 'primitives/felt'; import { isFelt } from 'primitives/segmentValue'; -import { VirtualMachine } from 'vm/virtualMachine'; +import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; import { @@ -67,23 +67,27 @@ export const initSquashData = ( bigKeys: CellRef, firstKey: CellRef ) => { - const dictAccessSize = 3; const address = vm.getPointer(...vm.extractBuffer(dictAccesses)); - if (Number(vm.getResOperandValue(ptrDiff)) % dictAccessSize) + + const ptrDiffValue = Number(vm.getResOperandValue(ptrDiff)); + if (ptrDiffValue % DICT_ACCESS_SIZE) throw new Error( 'Accessess array size must be divisible by the dict size (3)' ); + const nbAccesses = Number(vm.getResOperandValue(nAccesses)); for (let i = 0; i < nbAccesses; i++) { - const key = vm.memory.get(address.add(i * dictAccessSize)); + const key = vm.memory.get(address.add(i * DICT_ACCESS_SIZE)); if (!key || !isFelt(key)) throw new ExpectedFelt(key); vm.squashedDictManager.insert(key, new Felt(BigInt(i))); } + vm.squashedDictManager.keyToIndices.forEach((values, key) => { values.reverse(); vm.squashedDictManager.keys.push(new Felt(BigInt(key))); }); vm.squashedDictManager.keys.sort((a, b) => (a < b ? 1 : a > b ? -1 : 0)); + vm.memory.assertEq( vm.cellRefToRelocatable(bigKeys), vm.squashedDictManager.keys[0] > new Felt(1n << 128n) diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index ee3bff5a..7a9d5ac6 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -68,6 +68,8 @@ export class Dictionnary extends Map { } } +export const DICT_ACCESS_SIZE = 3; + export class VirtualMachine { private currentStep: bigint; memory: Memory; From 89a398e34971cc9a4f053afca85f9a8cc291406f Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 13:59:00 +0200 Subject: [PATCH 63/78] refactor: refactor initSquashData dict access size error --- src/errors/hints.ts | 12 ++++++++++++ src/hints/dict/initSquashData.ts | 5 ++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/errors/hints.ts b/src/errors/hints.ts index d718a38a..8e622e26 100644 --- a/src/errors/hints.ts +++ b/src/errors/hints.ts @@ -3,20 +3,32 @@ import { Hint } from 'hints/hintSchema'; class HintError extends Error {} +/** The provided register at `cell` is incorrect. It must either be AP or FP. */ export class InvalidCellRefRegister extends HintError { constructor(cell: CellRef) { super(`Invalid register, expected AP or FP, got ${cell}`); } } +/** The operator of the BinOp operation `op` must either be `Add` or `Mul`. */ export class InvalidOperation extends HintError { constructor(op: string) { super(`Invalid BinOp operator - Expected Add or Mul, received ${op}`); } } +/** The hint to be executed is unknown. */ export class UnknownHint extends HintError { constructor(hint: Hint) { super(`Unknown hint: ${hint}`); } } + +/** The number of dict accesses is invalid. */ +export class InvalidDictAccessesNumber extends HintError { + constructor(ptrDiffValue: number, dictAccessSize: number) { + super( + `The number of dictionnary accesses (${ptrDiffValue}) must be a multiple of the dictionnary access size (${dictAccessSize})` + ); + } +} diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index f7098cd3..edbf24f0 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -13,6 +13,7 @@ import { resOperand, ResOperand, } from 'hints/hintParamsSchema'; +import { InvalidDictAccessesNumber } from 'errors/hints'; /** Zod object to parse InitSquashData hint */ export const initSquashDataParser = z @@ -71,9 +72,7 @@ export const initSquashData = ( const ptrDiffValue = Number(vm.getResOperandValue(ptrDiff)); if (ptrDiffValue % DICT_ACCESS_SIZE) - throw new Error( - 'Accessess array size must be divisible by the dict size (3)' - ); + throw new InvalidDictAccessesNumber(ptrDiffValue, DICT_ACCESS_SIZE); const nbAccesses = Number(vm.getResOperandValue(nAccesses)); for (let i = 0; i < nbAccesses; i++) { From 4a12cdf12891edc5bf1cc150e08830fa5f9b364c Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 14:03:57 +0200 Subject: [PATCH 64/78] chore: typo --- src/errors/squashedDictManager.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/errors/squashedDictManager.ts b/src/errors/squashedDictManager.ts index 7738b119..b5c04e15 100644 --- a/src/errors/squashedDictManager.ts +++ b/src/errors/squashedDictManager.ts @@ -2,18 +2,18 @@ import { Felt } from 'primitives/felt'; class SquashedDictManagerError extends Error {} -/** There is no keys left in the squashed dictionnary manager */ +/** There are no keys left in the squashed dictionnary manager */ export class EmptyKeys extends SquashedDictManagerError { constructor() { - super('There is no keys left in the squashed dictionnary manager.'); + super('There are no keys left in the squashed dictionnary manager.'); } } -/** There is no indices at `key` */ +/** There are no indices at `key` */ export class EmptyIndices extends SquashedDictManagerError { constructor(key: Felt | undefined) { super( - `There is no indices at key ${ + `There are no indices at key ${ key ? key.toString() : key } in the squashed dictionnary manager.` ); From 60261a0b6d5b1a9f09f90bd67cb42676e560a8d6 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 14:46:37 +0200 Subject: [PATCH 65/78] doc: improve JSDoc AllocFelt252Dict --- src/hints/dict/allocFelt252Dict.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/hints/dict/allocFelt252Dict.ts b/src/hints/dict/allocFelt252Dict.ts index 7f7f0794..fe5fc172 100644 --- a/src/hints/dict/allocFelt252Dict.ts +++ b/src/hints/dict/allocFelt252Dict.ts @@ -2,7 +2,6 @@ import { z } from 'zod'; import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; -import { Felt } from 'primitives/felt'; import { isFelt, isRelocatable } from 'primitives/segmentValue'; import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine'; @@ -20,16 +19,23 @@ export const allocFelt252DictParser = z /** * AllocFelt252Dict hint * - * Add a new dictionnary. + * Allocates a new dictionary using the segment arena builtin. */ export type AllocFelt252Dict = z.infer; /** - * Add a new dictionnary. + * Allocates a new dictionary using the segment arena builtin. * - * - Extract the current number of dictionnaries: `dictNumber` - * - Extract the segment info pointer: `infoPtr` - * - Create a new dictionnary at `infoPtr + 3 * dictNumber` + * The segment arena builtin works by block of 3 cells: + * - The first cell contains the base address of the info segment. + * - The second cell contains the current number of allocated dictionaries. + * - The third cell contains the current number of squashed dictionaries. + * + * @param {VirtualMachine} vm - The virtual machine instance. + * @param {ResOperand} segmentArenaPtr - Pointer to the first cell of the next segment arena block. + * @throws {ExpectedFelt} If the number of dictionaries is not a valid Felt. + * @throws {ExpectedRelocatable} If the info pointer read is not a valid Relocatable. + */ export const allocFelt252Dict = ( vm: VirtualMachine, From 1a6c36acc05e82eb81bcaed6a161af81f480b290 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 14:51:50 +0200 Subject: [PATCH 66/78] chore: fix dictionary typo --- docs/howToImplementAHint.md | 4 ++-- src/builtins/builtin.ts | 2 +- src/errors/builtins.ts | 6 +++--- src/errors/hints.ts | 2 +- src/errors/squashedDictManager.ts | 10 +++++----- src/errors/virtualMachine.ts | 4 ++-- src/hints/dict/felt252DictEntryInit.ts | 4 ++-- src/hints/dict/felt252DictEntryUpdate.ts | 2 +- src/hints/dict/getCurrentAccessIndex.ts | 2 +- src/hints/dict/getSegmentArenaIndex.ts | 8 ++++---- src/hints/dict/initSquashData.ts | 4 ++-- src/hints/scopeManager.ts | 2 +- src/hints/squashedDictManager.ts | 8 ++++---- src/runners/layout.ts | 4 ++-- src/vm/virtualMachine.test.ts | 10 +++++----- src/vm/virtualMachine.ts | 16 ++++++++-------- 16 files changed, 44 insertions(+), 44 deletions(-) diff --git a/docs/howToImplementAHint.md b/docs/howToImplementAHint.md index 0d5734a5..4573c866 100644 --- a/docs/howToImplementAHint.md +++ b/docs/howToImplementAHint.md @@ -54,7 +54,7 @@ The GetSegmentArenaIndex hint can be found in a format similar to this one: - Create the file `src/hints/dict/getSegmentArenaIndex.ts`. Place the file in the appropriate sub-folder category, here `dict` because the hint is dedicated - to dictionnaries. + to dictionaries. - Create and export a Zod object `getSegmentArenaIndexParser` which follows the hint signature: @@ -118,7 +118,7 @@ The last step is adding the hint to the handler object. The handler is defined in `src/hints/hintHandler.ts` -It is a dictionnary which maps a `HintName` value to a function executing the +It is a dictionary which maps a `HintName` value to a function executing the corresponding core logic function. ```typescript diff --git a/src/builtins/builtin.ts b/src/builtins/builtin.ts index e881df46..36bf8dd7 100644 --- a/src/builtins/builtin.ts +++ b/src/builtins/builtin.ts @@ -21,7 +21,7 @@ export type BuiltinHandler = ProxyHandler>; * - Keccak: Builtin for keccak hash family * - Pedersen: Builtin for pedersen hash family * - Poseidon: Builtin for poseidon hash family - * - Segment Arena: Builtin to manage the dictionnaries + * - Segment Arena: Builtin to manage the dictionaries * - Output: Output builtin */ const BUILTIN_HANDLER: { diff --git a/src/errors/builtins.ts b/src/errors/builtins.ts index 78dab357..407f017e 100644 --- a/src/errors/builtins.ts +++ b/src/errors/builtins.ts @@ -22,11 +22,11 @@ export class RangeCheckOutOfBounds extends BuiltinError { } } -/** ECDSA signature cannot be retrieved from dictionnary at `offset` */ +/** ECDSA signature cannot be retrieved from dictionary at `offset` */ export class UndefinedECDSASignature extends BuiltinError { constructor(offset: number) { super( - `ECDSA signature cannot be retrieved from dictionnary at offset ${offset}` + `ECDSA signature cannot be retrieved from dictionary at offset ${offset}` ); } } @@ -48,7 +48,7 @@ public key (negative): ${pubKeyNegHex} } } -/** The signature dictionnary is undefined */ +/** The signature dictionary is undefined */ export class UndefinedSignatureDict extends BuiltinError {} /** An offset of type number is expected */ diff --git a/src/errors/hints.ts b/src/errors/hints.ts index 8e622e26..050b1b27 100644 --- a/src/errors/hints.ts +++ b/src/errors/hints.ts @@ -28,7 +28,7 @@ export class UnknownHint extends HintError { export class InvalidDictAccessesNumber extends HintError { constructor(ptrDiffValue: number, dictAccessSize: number) { super( - `The number of dictionnary accesses (${ptrDiffValue}) must be a multiple of the dictionnary access size (${dictAccessSize})` + `The number of dictionary accesses (${ptrDiffValue}) must be a multiple of the dictionary access size (${dictAccessSize})` ); } } diff --git a/src/errors/squashedDictManager.ts b/src/errors/squashedDictManager.ts index b5c04e15..0e31acf1 100644 --- a/src/errors/squashedDictManager.ts +++ b/src/errors/squashedDictManager.ts @@ -2,10 +2,10 @@ import { Felt } from 'primitives/felt'; class SquashedDictManagerError extends Error {} -/** There are no keys left in the squashed dictionnary manager */ +/** There are no keys left in the squashed dictionary manager */ export class EmptyKeys extends SquashedDictManagerError { constructor() { - super('There are no keys left in the squashed dictionnary manager.'); + super('There are no keys left in the squashed dictionary manager.'); } } @@ -15,14 +15,14 @@ export class EmptyIndices extends SquashedDictManagerError { super( `There are no indices at key ${ key ? key.toString() : key - } in the squashed dictionnary manager.` + } in the squashed dictionary manager.` ); } } -/** The last index of the squashed dictionnary manager is empty. */ +/** The last index of the squashed dictionary manager is empty. */ export class EmptyIndex extends SquashedDictManagerError { constructor() { - super('The last index of the squashed dictionnary manager is empty.'); + super('The last index of the squashed dictionary manager is empty.'); } } diff --git a/src/errors/virtualMachine.ts b/src/errors/virtualMachine.ts index db60df9a..9f2a2a52 100644 --- a/src/errors/virtualMachine.ts +++ b/src/errors/virtualMachine.ts @@ -66,9 +66,9 @@ export class InvalidBufferResOp extends VirtualMachineError { } } -/** Cannot find Dictionnary at `address` */ +/** Cannot find Dictionary at `address` */ export class DictNotFound extends VirtualMachineError { constructor(address: Relocatable) { - super(`Cannot found any Dictionnary at address ${address.toString()}`); + super(`Cannot found any Dictionary at address ${address.toString()}`); } } diff --git a/src/hints/dict/felt252DictEntryInit.ts b/src/hints/dict/felt252DictEntryInit.ts index f82b669d..c6ec52e5 100644 --- a/src/hints/dict/felt252DictEntryInit.ts +++ b/src/hints/dict/felt252DictEntryInit.ts @@ -23,12 +23,12 @@ export const felt252DictEntryInitParser = z /** * Felt252DictEntryInit hint * - * Initialize the entry to be added to a given dictionnary. + * Initialize the entry to be added to a given dictionary. */ export type Felt252DictEntryInit = z.infer; /** - * Get the dictionnary `dict` at `dictPtr` + * Get the dictionary `dict` at `dictPtr` * Get the `key` to be initialized * * Read the current value at `key` if any, diff --git a/src/hints/dict/felt252DictEntryUpdate.ts b/src/hints/dict/felt252DictEntryUpdate.ts index 29096421..09609bf5 100644 --- a/src/hints/dict/felt252DictEntryUpdate.ts +++ b/src/hints/dict/felt252DictEntryUpdate.ts @@ -32,7 +32,7 @@ export type Felt252DictEntryUpdate = z.infer< >; /** - * Get the dictionnary `dict` at `dictPtr` + * Get the dictionary `dict` at `dictPtr` * * Get the key at address `dict - 3` * diff --git a/src/hints/dict/getCurrentAccessIndex.ts b/src/hints/dict/getCurrentAccessIndex.ts index d8771ef3..02ec03d3 100644 --- a/src/hints/dict/getCurrentAccessIndex.ts +++ b/src/hints/dict/getCurrentAccessIndex.ts @@ -17,7 +17,7 @@ export const getCurrentAccessIndexParser = z export type GetCurrentAccessIndex = z.infer; /** - * Assert that the biggest key of the squashed dictionnary is < 2 ** 128 + * Assert that the biggest key of the squashed dictionary is < 2 ** 128 */ export const getCurrentAccessIndex = ( vm: VirtualMachine, diff --git a/src/hints/dict/getSegmentArenaIndex.ts b/src/hints/dict/getSegmentArenaIndex.ts index 121527b1..0d94943b 100644 --- a/src/hints/dict/getSegmentArenaIndex.ts +++ b/src/hints/dict/getSegmentArenaIndex.ts @@ -28,16 +28,16 @@ export const getSegmentArenaIndexParser = z * GetSegmentArenaIndex hint * * Retrieve the index of the given dict in the info segment. - * Used when finalizing the dictionnaries. + * Used when finalizing the dictionaries. */ export type GetSegmentArenaIndex = z.infer; /** * Assert that `dictIndex` address stores the identifier of the - * dictionnary found at `dictEndPtr`. + * dictionary found at `dictEndPtr`. * - * The identifier `id` the is dictionnary number, - * n for the n-th dictionnary, starting at 0. + * The identifier `id` the is dictionary number, + * n for the n-th dictionary, starting at 0. * */ export const getSegmentArenaIndex = ( diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index edbf24f0..2b4078da 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -48,12 +48,12 @@ export const initSquashDataParser = z /** * InitSquashData hint * - * Initialize the squashed dictionnary data. + * Initialize the squashed dictionary data. */ export type InitSquashData = z.infer; /** - * Initialize the squashed dictionnary + * Initialize the squashed dictionary * * - Insert all accessed keys * - Assert the biggest key to `bigKeys` address diff --git a/src/hints/scopeManager.ts b/src/hints/scopeManager.ts index a7863532..dc611c57 100644 --- a/src/hints/scopeManager.ts +++ b/src/hints/scopeManager.ts @@ -1,7 +1,7 @@ import { VariableNotInScope, CannotExitMainScope } from 'errors/scopeManager'; /** - * A dictionnary mapping a variable name to its value, + * A dictionary mapping a variable name to its value, * which can be anything */ export type Scope = { diff --git a/src/hints/squashedDictManager.ts b/src/hints/squashedDictManager.ts index 69c29cd1..cce0a139 100644 --- a/src/hints/squashedDictManager.ts +++ b/src/hints/squashedDictManager.ts @@ -7,10 +7,10 @@ import { import { Felt } from 'primitives/felt'; /** - * Handle the squashing of dictionnaries. + * Handle the squashing of dictionaries. */ export class SquashedDictManager { - /** Maps the key of a dictionnary to its taken values accross the run. */ + /** Maps the key of a dictionary to its taken values accross the run. */ public keyToIndices: Map; /** An array containing the keys that still need to be squashed. */ public keys: Felt[]; @@ -31,14 +31,14 @@ export class SquashedDictManager { } } - /** Return the last key of the dictionnary. */ + /** Return the last key of the dictionary. */ lastKey(): Felt { const len = this.keys.length; if (!len) throw new EmptyKeys(); return this.keys[len - 1]; } - /** Remove and return the last key of the dictionnary. */ + /** Remove and return the last key of the dictionary. */ popKey(): Felt { const key = this.keys.pop(); if (!key) throw new EmptyKeys(); diff --git a/src/runners/layout.ts b/src/runners/layout.ts index f8eecbaa..d92c2485 100644 --- a/src/runners/layout.ts +++ b/src/runners/layout.ts @@ -14,7 +14,7 @@ export type DilutedPool = { * - rcUnits: Range Check Units. * - publicMemoryFraction: The ratios total memory cells over public memory cells. * - dilutedPool: The diluted pool, used for additionnal checks on Bitwise & Keccak - * - ratios: Dictionnary mapping each builtin name to its ratio. + * - ratios: Dictionary mapping each builtin name to its ratio. * * NOTE: The builtins `segment_arena`, `gas_builtin` and `system` which can be found * in the program builtins are not part of the layouts because they're not proven as such. @@ -38,7 +38,7 @@ export const DEFAULT_DILUTED_POOL: DilutedPool = { }; /** - * Dictionnary containing all the available layouts: + * Dictionary containing all the available layouts: * - plain * - small * - dex diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index 97b8eed2..e9cdfeb7 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -19,7 +19,7 @@ import { FpUpdate, Op1Src, } from './instruction'; -import { Dictionnary, VirtualMachine } from './virtualMachine'; +import { Dictionary, VirtualMachine } from './virtualMachine'; import { CellRef, Operation, OpType, ResOperand } from 'hints/hintParamsSchema'; const instructions = { @@ -742,20 +742,20 @@ describe('VirtualMachine', () => { }); }); - describe('Dictionnary', () => { + describe('Dictionary', () => { test('should properly initialize the dict manager', () => { const vm = new VirtualMachine(); expect(vm.dictManager.size).toEqual(0); }); - test('should properly create a new dictionnary', () => { + test('should properly create a new dictionary', () => { const vm = new VirtualMachine(); const address = vm.newDict(); expect(address).toEqual(new Relocatable(0, 0)); - expect(vm.getDict(address)).toEqual(new Dictionnary(new Felt(0n))); + expect(vm.getDict(address)).toEqual(new Dictionary(new Felt(0n))); }); - test('should throw DictNotFound when accessing a non-existing dictionnary', () => { + test('should throw DictNotFound when accessing a non-existing dictionary', () => { const vm = new VirtualMachine(); const address = new Relocatable(2, 3); expect(() => vm.getDict(address)).toThrow(new DictNotFound(address)); diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index 7a9d5ac6..b4f5b8ff 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -61,7 +61,7 @@ export type RelocatedMemory = { value: Felt; }; -export class Dictionnary extends Map { +export class Dictionary extends Map { constructor(public readonly id: Felt) { super(); this.id = id; @@ -76,7 +76,7 @@ export class VirtualMachine { pc: Relocatable; ap: Relocatable; fp: Relocatable; - dictManager: Map; + dictManager: Map; squashedDictManager: SquashedDictManager; scopeManager: ScopeManager; trace: TraceEntry[]; @@ -97,7 +97,7 @@ export class VirtualMachine { this.fp = new Relocatable(1, 0); this.scopeManager = new ScopeManager(); - this.dictManager = new Map(); + this.dictManager = new Map(); this.squashedDictManager = new SquashedDictManager(); } @@ -637,7 +637,7 @@ export class VirtualMachine { } /** - * Creates a new dictionnary. + * Creates a new dictionary. * * NOTE: used in Cairo hints */ @@ -645,19 +645,19 @@ export class VirtualMachine { const dictAddr = this.memory.addSegment(); this.dictManager.set( dictAddr.segmentId, - new Dictionnary(new Felt(BigInt(this.dictManager.size))) + new Dictionary(new Felt(BigInt(this.dictManager.size))) ); return dictAddr; } /** - * Return the dictionnary at `address` + * Return the dictionary at `address` * - * Throw if dictionnary was not found + * Throw if dictionary was not found * * NOTE: used in Cairo hints */ - getDict(address: Relocatable): Dictionnary { + getDict(address: Relocatable): Dictionary { const dict = this.dictManager.get(address.segmentId); if (!dict) throw new DictNotFound(address); return dict; From 80e05ca68d9b27726f5e567984d9b0ee02055e35 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 15:19:14 +0200 Subject: [PATCH 67/78] feat: add compare method to Felt class --- src/hints/assertLeFindSmallArc.ts | 2 +- src/hints/dict/initSquashData.ts | 2 +- src/primitives/felt.test.ts | 12 ++++++++++++ src/primitives/felt.ts | 11 +++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/hints/assertLeFindSmallArc.ts b/src/hints/assertLeFindSmallArc.ts index fe325d64..34987836 100644 --- a/src/hints/assertLeFindSmallArc.ts +++ b/src/hints/assertLeFindSmallArc.ts @@ -55,7 +55,7 @@ export const assertLeFindSmallArcs = ( { value: aVal, pos: 0 }, { value: bVal.sub(aVal), pos: 1 }, { value: new Felt(-1n).sub(bVal), pos: 2 }, - ].sort((a, b) => (a.value > b.value ? 1 : a.value < b.value ? -1 : 0)); + ].sort((a, b) => a.value.compare(b.value)); vm.scopeManager.set('excluded_arc', arcs[2].pos); diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index 2b4078da..129eefcc 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -85,7 +85,7 @@ export const initSquashData = ( values.reverse(); vm.squashedDictManager.keys.push(new Felt(BigInt(key))); }); - vm.squashedDictManager.keys.sort((a, b) => (a < b ? 1 : a > b ? -1 : 0)); + vm.squashedDictManager.keys.sort((a, b) => b.compare(a)); vm.memory.assertEq( vm.cellRefToRelocatable(bigKeys), diff --git a/src/primitives/felt.test.ts b/src/primitives/felt.test.ts index c58ca9e2..e0c4a09d 100644 --- a/src/primitives/felt.test.ts +++ b/src/primitives/felt.test.ts @@ -139,4 +139,16 @@ describe('Felt', () => { expect(result).toStrictEqual(a); }); }); + + describe('compare', () => { + test.each([ + [new Felt(1n), new Felt(0n), 1], + [new Felt(1n), new Felt(1n), 0], + [new Felt(0n), new Felt(1n), -1], + [new Felt(-1n), new Felt(-10n), 1], + [new Felt(-1n), new Felt(Felt.PRIME - 1n), 0], + ])('should properly compare two Felt', (a, b, expected) => { + expect(a.compare(b)).toEqual(expected); + }); + }); }); diff --git a/src/primitives/felt.ts b/src/primitives/felt.ts index d6b5da44..e9e19654 100644 --- a/src/primitives/felt.ts +++ b/src/primitives/felt.ts @@ -101,6 +101,17 @@ export class Felt { return !isRelocatable(other) && this.inner === other.inner; } + /** + * Compare two Felt. + * + * @param other - The Felt to compare against. + * @returns {number} A positive value if `this > other`, + * a negative value if `this < other` and zero if they're equal. + */ + compare(other: Felt): number { + return this > other ? 1 : this < other ? -1 : 0; + } + sqrt(): Felt { return new Felt(CURVE.Fp.sqrt(this.inner)); } From c4897390f7faf752983cb711f15211aa1177d626 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 17:24:32 +0200 Subject: [PATCH 68/78] refactor: use named constants for dict and segment arena related offset in hints --- src/builtins/segmentArena.ts | 27 +++++++++++++++++++++++ src/errors/dictionary.ts | 10 +++++++++ src/errors/virtualMachine.ts | 7 ------ src/hints/dict/allocFelt252Dict.ts | 8 ++++--- src/hints/dict/felt252DictEntryInit.ts | 3 ++- src/hints/dict/felt252DictEntryUpdate.ts | 5 +++-- src/hints/dict/initSquashData.ts | 5 +++-- src/hints/dictionary.test.ts | 28 ++++++++++++++++++++++++ src/hints/dictionary.ts | 27 +++++++++++++++++++++++ src/vm/virtualMachine.test.ts | 28 ++---------------------- src/vm/virtualMachine.ts | 17 +++++--------- 11 files changed, 112 insertions(+), 53 deletions(-) create mode 100644 src/errors/dictionary.ts create mode 100644 src/hints/dictionary.test.ts create mode 100644 src/hints/dictionary.ts diff --git a/src/builtins/segmentArena.ts b/src/builtins/segmentArena.ts index e4b4b687..e67c06d7 100644 --- a/src/builtins/segmentArena.ts +++ b/src/builtins/segmentArena.ts @@ -1,3 +1,30 @@ import { BuiltinHandler } from './builtin'; +/** + * Offset to compute the address + * of the info segment pointer. + */ +export const INFO_PTR_OFFSET = 3; + +/** + * Offset to read the current number + * of allocated dictionaries. + */ +export const DICT_NUMBER_OFFSET = 2; + +/** + * The segment arena builtin manages Cairo dictionaries. + * + * It works by block of 3 cells: + * - The first cell contains the base address of the info pointer. + * - The second cell contains the current number of allocated dictionaries. + * - The third cell contains the current number of squashed dictionaries. + * + * The Info segment is tightly closed to the segment arena builtin. + * + * It also works by block of 3 cells: + * - The first cell is the base address of a dictionary + * - The second cell is the end address of a dictionary when squashed. + * - The third cell is the current number of squashed dictionaries (i.e. its squashing index). + */ export const segmentArenaHandler: BuiltinHandler = {}; diff --git a/src/errors/dictionary.ts b/src/errors/dictionary.ts new file mode 100644 index 00000000..ad83c3e2 --- /dev/null +++ b/src/errors/dictionary.ts @@ -0,0 +1,10 @@ +import { Relocatable } from 'primitives/relocatable'; + +class DictionaryError extends Error {} + +/** Cannot find Dictionary at `address` */ +export class DictNotFound extends DictionaryError { + constructor(address: Relocatable) { + super(`Cannot found any Dictionary at address ${address.toString()}`); + } +} diff --git a/src/errors/virtualMachine.ts b/src/errors/virtualMachine.ts index 9f2a2a52..4542882a 100644 --- a/src/errors/virtualMachine.ts +++ b/src/errors/virtualMachine.ts @@ -65,10 +65,3 @@ export class InvalidBufferResOp extends VirtualMachineError { super(`Cannot extract buffer from the given ResOperand: ${resOperand}`); } } - -/** Cannot find Dictionary at `address` */ -export class DictNotFound extends VirtualMachineError { - constructor(address: Relocatable) { - super(`Cannot found any Dictionary at address ${address.toString()}`); - } -} diff --git a/src/hints/dict/allocFelt252Dict.ts b/src/hints/dict/allocFelt252Dict.ts index fe5fc172..afebc058 100644 --- a/src/hints/dict/allocFelt252Dict.ts +++ b/src/hints/dict/allocFelt252Dict.ts @@ -3,10 +3,12 @@ import { z } from 'zod'; import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; import { isFelt, isRelocatable } from 'primitives/segmentValue'; -import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine'; +import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; import { resOperand, ResOperand } from 'hints/hintParamsSchema'; +import { DICT_ACCESS_SIZE } from 'hints/dictionary'; +import { DICT_NUMBER_OFFSET, INFO_PTR_OFFSET } from 'builtins/segmentArena'; /** Zod object to parse AllocFelt252Dict hint */ export const allocFelt252DictParser = z @@ -42,10 +44,10 @@ export const allocFelt252Dict = ( segmentArenaPtr: ResOperand ) => { const arenaPtr = vm.getPointer(...vm.extractBuffer(segmentArenaPtr)); - const dictNumber = vm.memory.get(arenaPtr.sub(2)); + const dictNumber = vm.memory.get(arenaPtr.sub(DICT_NUMBER_OFFSET)); if (!dictNumber || !isFelt(dictNumber)) throw new ExpectedFelt(dictNumber); - const infoPtr = vm.memory.get(arenaPtr.sub(3)); + const infoPtr = vm.memory.get(arenaPtr.sub(INFO_PTR_OFFSET)); if (!infoPtr || !isRelocatable(infoPtr)) throw new ExpectedRelocatable(infoPtr); diff --git a/src/hints/dict/felt252DictEntryInit.ts b/src/hints/dict/felt252DictEntryInit.ts index c6ec52e5..4a951ed2 100644 --- a/src/hints/dict/felt252DictEntryInit.ts +++ b/src/hints/dict/felt252DictEntryInit.ts @@ -5,6 +5,7 @@ import { VirtualMachine } from 'vm/virtualMachine'; import { resOperand, ResOperand } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; +import { PREV_VALUE_OFFSET } from 'hints/dictionary'; /** Zod object to parse Felt252DictEntryInit hint */ export const felt252DictEntryInitParser = z @@ -47,5 +48,5 @@ export const felt252DictEntryInit = ( const dict = vm.getDict(address); const prevValue = dict.get(keyValue) || new Felt(0n); dict.set(keyValue, prevValue); - vm.memory.assertEq(address.add(1), prevValue); + vm.memory.assertEq(address.add(PREV_VALUE_OFFSET), prevValue); }; diff --git a/src/hints/dict/felt252DictEntryUpdate.ts b/src/hints/dict/felt252DictEntryUpdate.ts index 09609bf5..e106ab6e 100644 --- a/src/hints/dict/felt252DictEntryUpdate.ts +++ b/src/hints/dict/felt252DictEntryUpdate.ts @@ -3,10 +3,11 @@ import { z } from 'zod'; import { ExpectedFelt } from 'errors/primitives'; import { isFelt } from 'primitives/segmentValue'; -import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine'; +import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; import { Deref, OpType, resOperand, ResOperand } from 'hints/hintParamsSchema'; +import { KEY_OFFSET } from 'hints/dictionary'; /** Zod object to parse Felt252DictEntryUpdate hint */ export const felt252DictEntryUpdateParser = z @@ -45,7 +46,7 @@ export const felt252DictEntryUpdate = ( value: ResOperand ) => { const address = vm.getPointer(...vm.extractBuffer(dictPtr)); - const key = vm.memory.get(address.sub(DICT_ACCESS_SIZE)); + const key = vm.memory.get(address.sub(KEY_OFFSET)); if (!key || !isFelt(key)) throw new ExpectedFelt(key); const val = value.type === OpType.Deref diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index 129eefcc..35a7dcfc 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -1,10 +1,11 @@ import { z } from 'zod'; import { ExpectedFelt } from 'errors/primitives'; +import { InvalidDictAccessesNumber } from 'errors/hints'; import { Felt } from 'primitives/felt'; import { isFelt } from 'primitives/segmentValue'; -import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine'; +import { VirtualMachine } from 'vm/virtualMachine'; import { HintName } from 'hints/hintName'; import { @@ -13,7 +14,7 @@ import { resOperand, ResOperand, } from 'hints/hintParamsSchema'; -import { InvalidDictAccessesNumber } from 'errors/hints'; +import { DICT_ACCESS_SIZE } from 'hints/dictionary'; /** Zod object to parse InitSquashData hint */ export const initSquashDataParser = z diff --git a/src/hints/dictionary.test.ts b/src/hints/dictionary.test.ts new file mode 100644 index 00000000..aadf74d3 --- /dev/null +++ b/src/hints/dictionary.test.ts @@ -0,0 +1,28 @@ +import { describe, test, expect } from 'bun:test'; + +import { DictNotFound } from 'errors/dictionary'; + +import { Felt } from 'primitives/felt'; +import { Relocatable } from 'primitives/relocatable'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { Dictionary } from './dictionary'; + +describe('Dictionary', () => { + test('should properly initialize the dict manager', () => { + const vm = new VirtualMachine(); + expect(vm.dictManager.size).toEqual(0); + }); + + test('should properly create a new dictionary', () => { + const vm = new VirtualMachine(); + const address = vm.newDict(); + expect(address).toEqual(new Relocatable(0, 0)); + expect(vm.getDict(address)).toEqual(new Dictionary(new Felt(0n))); + }); + + test('should throw DictNotFound when accessing a non-existing dictionary', () => { + const vm = new VirtualMachine(); + const address = new Relocatable(2, 3); + expect(() => vm.getDict(address)).toThrow(new DictNotFound(address)); + }); +}); diff --git a/src/hints/dictionary.ts b/src/hints/dictionary.ts new file mode 100644 index 00000000..57f35353 --- /dev/null +++ b/src/hints/dictionary.ts @@ -0,0 +1,27 @@ +import { Felt } from 'primitives/felt'; +import { SegmentValue } from 'primitives/segmentValue'; + +export const DICT_ACCESS_SIZE = 3; + +/** Offset to read the key of the entry to update. */ +export const KEY_OFFSET = 3; + +/** + * Offset to read the previous value + * of the entry to read or update. + */ +export const PREV_VALUE_OFFSET = 1; + +/** + * Helper class to implement Cairo dictionaries. + * + * The `id` attribute is needed to keep track + * of the multiple dictionaries and their + * corresponding segment in memory. + */ +export class Dictionary extends Map { + constructor(public readonly id: Felt) { + super(); + this.id = id; + } +} diff --git a/src/vm/virtualMachine.test.ts b/src/vm/virtualMachine.test.ts index e9cdfeb7..3718179b 100644 --- a/src/vm/virtualMachine.test.ts +++ b/src/vm/virtualMachine.test.ts @@ -1,11 +1,7 @@ import { test, expect, describe, spyOn } from 'bun:test'; import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives'; -import { - DictNotFound, - InvalidBufferResOp, - UnusedRes, -} from 'errors/virtualMachine'; +import { InvalidBufferResOp, UnusedRes } from 'errors/virtualMachine'; import { Felt } from 'primitives/felt'; import { Relocatable } from 'primitives/relocatable'; @@ -19,7 +15,7 @@ import { FpUpdate, Op1Src, } from './instruction'; -import { Dictionary, VirtualMachine } from './virtualMachine'; +import { VirtualMachine } from './virtualMachine'; import { CellRef, Operation, OpType, ResOperand } from 'hints/hintParamsSchema'; const instructions = { @@ -741,24 +737,4 @@ describe('VirtualMachine', () => { ); }); }); - - describe('Dictionary', () => { - test('should properly initialize the dict manager', () => { - const vm = new VirtualMachine(); - expect(vm.dictManager.size).toEqual(0); - }); - - test('should properly create a new dictionary', () => { - const vm = new VirtualMachine(); - const address = vm.newDict(); - expect(address).toEqual(new Relocatable(0, 0)); - expect(vm.getDict(address)).toEqual(new Dictionary(new Felt(0n))); - }); - - test('should throw DictNotFound when accessing a non-existing dictionary', () => { - const vm = new VirtualMachine(); - const address = new Relocatable(2, 3); - expect(() => vm.getDict(address)).toThrow(new DictNotFound(address)); - }); - }); }); diff --git a/src/vm/virtualMachine.ts b/src/vm/virtualMachine.ts index b4f5b8ff..f33ae157 100644 --- a/src/vm/virtualMachine.ts +++ b/src/vm/virtualMachine.ts @@ -9,15 +9,16 @@ import { UndefinedOp0, InvalidCallOp0Value, UndefinedOp1, - DictNotFound, InvalidBufferResOp, } from 'errors/virtualMachine'; +import { DictNotFound } from 'errors/dictionary'; import { InvalidCellRefRegister, UnknownHint } from 'errors/hints'; import { Felt } from 'primitives/felt'; import { Relocatable } from 'primitives/relocatable'; import { SegmentValue, isFelt, isRelocatable } from 'primitives/segmentValue'; import { Memory } from 'memory/memory'; + import { BinOp, CellRef, @@ -29,9 +30,10 @@ import { ResOperand, } from 'hints/hintParamsSchema'; import { Hint } from 'hints/hintSchema'; -import { ScopeManager } from 'hints/scopeManager'; -import { SquashedDictManager } from '../hints/squashedDictManager'; import { handlers, HintHandler } from 'hints/hintHandler'; +import { Dictionary } from 'hints/dictionary'; +import { ScopeManager } from 'hints/scopeManager'; +import { SquashedDictManager } from 'hints/squashedDictManager'; import { ApUpdate, @@ -61,15 +63,6 @@ export type RelocatedMemory = { value: Felt; }; -export class Dictionary extends Map { - constructor(public readonly id: Felt) { - super(); - this.id = id; - } -} - -export const DICT_ACCESS_SIZE = 3; - export class VirtualMachine { private currentStep: bigint; memory: Memory; From 2991bcf0cd9d0ff15427ff88c148e2177b5c9a00 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 17:31:23 +0200 Subject: [PATCH 69/78] refactor: rename array of segment arena initial values --- src/runners/cairoRunner.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/runners/cairoRunner.ts b/src/runners/cairoRunner.ts index a916df73..12b552d2 100644 --- a/src/runners/cairoRunner.ts +++ b/src/runners/cairoRunner.ts @@ -68,16 +68,16 @@ export class CairoRunner { const builtin_stack = builtins.map((builtin) => { const handler = getBuiltin(builtin); if (builtin === 'segment_arena') { - const info = [ + const initialValues = [ this.vm.memory.addSegment(handler), new Felt(0n), new Felt(0n), ]; const base = this.vm.memory.addSegment(handler); - info.map((value, offset) => + initialValues.map((value, offset) => this.vm.memory.assertEq(base.add(offset), value) ); - return base.add(info.length); + return base.add(initialValues.length); } return this.vm.memory.addSegment(handler); }); From 5f5f38e486f5ef728c20c4ff58b46722c785bd4e Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:00:46 +0200 Subject: [PATCH 70/78] doc: improve JSDoc Felt252DictEntryInit --- src/hints/dict/felt252DictEntryInit.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/hints/dict/felt252DictEntryInit.ts b/src/hints/dict/felt252DictEntryInit.ts index 4a951ed2..9c97f3ee 100644 --- a/src/hints/dict/felt252DictEntryInit.ts +++ b/src/hints/dict/felt252DictEntryInit.ts @@ -24,19 +24,24 @@ export const felt252DictEntryInitParser = z /** * Felt252DictEntryInit hint * - * Initialize the entry to be added to a given dictionary. + * Initializes a dictionary access by asserting that + * the previous value cell holds the current value + * of the dictionary (initialized to zero if undefined). + * */ export type Felt252DictEntryInit = z.infer; /** - * Get the dictionary `dict` at `dictPtr` - * Get the `key` to be initialized - * - * Read the current value at `key` if any, - * Initialize `dict[key]` to 0 if undefined. + * Initializes a dictionary access to a key by asserting that + * the previous value cell contains the current value + * of this dictionary key. * - * Assert the value at `key` to the `dict` segment. + * If it is the first entry for this key, + * the previous value will be zero. * + * @param {VirtualMachine} vm - The virtual machine instance. + * @param {ResOperand} dictPtr - Pointer to the dictionary access to be initialized. + * @param key - The dictionary key to access to. */ export const felt252DictEntryInit = ( vm: VirtualMachine, From 9075b6bd359f406593b2e52ae42eab7ac9097c51 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:00:50 +0200 Subject: [PATCH 71/78] doc: improve JSDoc Felt252DictEntryUpdate --- src/hints/dict/felt252DictEntryUpdate.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/hints/dict/felt252DictEntryUpdate.ts b/src/hints/dict/felt252DictEntryUpdate.ts index e106ab6e..01e2bd22 100644 --- a/src/hints/dict/felt252DictEntryUpdate.ts +++ b/src/hints/dict/felt252DictEntryUpdate.ts @@ -26,19 +26,18 @@ export const felt252DictEntryUpdateParser = z /** * Felt252DictEntryUpdate hint * - * Update an existing dict entry `key` to `value`: `dict[key] = value` + * Updates a dictionary entry. */ export type Felt252DictEntryUpdate = z.infer< typeof felt252DictEntryUpdateParser >; /** - * Get the dictionary `dict` at `dictPtr` - * - * Get the key at address `dict - 3` - * - * `dict[key] = value` + * Updates a dictionary entry. * + * @param {VirtualMachine} vm - The virtual machine instance. + * @param {ResOperand} dictPtr - Pointer to the next dictionary access. + * @param {ResOperand} value - The new value to be set. */ export const felt252DictEntryUpdate = ( vm: VirtualMachine, From 6b8c2193b3a95647607f046a9155e8bc5852db48 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:35:45 +0200 Subject: [PATCH 72/78] doc: improve JSDoc GetCurrentAccessDelta --- src/hints/dict/getCurrentAccessDelta.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/hints/dict/getCurrentAccessDelta.ts b/src/hints/dict/getCurrentAccessDelta.ts index f7691f7c..bb83c0f0 100644 --- a/src/hints/dict/getCurrentAccessDelta.ts +++ b/src/hints/dict/getCurrentAccessDelta.ts @@ -16,13 +16,16 @@ export const getCurrentAccessDeltaParser = z /** * GetCurrentAccessDelta hint * - * Assert to memory the difference between the current index to the next one. + * Computes the delta between the last two indices. * */ export type GetCurrentAccessDelta = z.infer; /** - * Assert `currIndex - prevIndex - 1` to `indexDeltaMinusOne` + * Computes the delta between the last two indices. + * + * @param {VirtualMachine} vm - The virtual machine instance + * @param {CellRef} indexDeltaMinusOne - The address where the index delta should be asserted. */ export const getCurrentAccessDelta = ( vm: VirtualMachine, From 13130c33c6746c004672f0c6f2257fba7471d823 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:35:57 +0200 Subject: [PATCH 73/78] doc: improve JSDoc GetCurrentAccessIndex --- src/hints/dict/getCurrentAccessIndex.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/hints/dict/getCurrentAccessIndex.ts b/src/hints/dict/getCurrentAccessIndex.ts index 02ec03d3..d853787c 100644 --- a/src/hints/dict/getCurrentAccessIndex.ts +++ b/src/hints/dict/getCurrentAccessIndex.ts @@ -13,11 +13,21 @@ export const getCurrentAccessIndexParser = z rangeCheckPtr: range_check_ptr, })); -/** GetCurrentAccessIndex hint */ +/** + * GetCurrentAccessIndex hint + * Assert that the accessed index is strictly inferior to 2 ** 128. + * + * A dictionary cannot have more than 2 ** 128 accesses in a single run. + */ export type GetCurrentAccessIndex = z.infer; /** - * Assert that the biggest key of the squashed dictionary is < 2 ** 128 + * Assert that the accessed index is strictly inferior to 2 ** 128. + * + * A dictionary cannot have more than 2 ** 128 accesses in a single run. + * + * @param {VirtualMachine} vm - The virtual machine instance + * @param {ResOperand} rangeCheckPtr - Pointer to the range check builtin. */ export const getCurrentAccessIndex = ( vm: VirtualMachine, From 0ec3f16b1d0da984418d244951228d6b66aab757 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:36:05 +0200 Subject: [PATCH 74/78] doc: improve JSDoc GetNextDictKey --- src/hints/dict/getNextDictKey.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/hints/dict/getNextDictKey.ts b/src/hints/dict/getNextDictKey.ts index 8b4b5486..4380096c 100644 --- a/src/hints/dict/getNextDictKey.ts +++ b/src/hints/dict/getNextDictKey.ts @@ -13,11 +13,18 @@ export const getNextDictKeyParser = z nextKey: next_key, })); -/** GetNextDictKey hint */ +/** + * GetNextDictKey hint + * + * Get the next key to be squashed. + */ export type GetNextDictKey = z.infer; /** - * Pop the current key and assert the next one to `nextKey` + * Get the next key to be squashed. + * + * @param {VirtualMachine} vm - The virtual machine instance + * @param {CellRef} nextKey - Address to store the next key to be squashed. */ export const getNextDictKey = (vm: VirtualMachine, nextKey: CellRef) => { vm.squashedDictManager.popKey(); From 94a5d3d5efb03ae5356004f54e7b7d998b73087e Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:36:14 +0200 Subject: [PATCH 75/78] doc: improve JSDoc GetSegmentArenaIndex --- src/hints/dict/getSegmentArenaIndex.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hints/dict/getSegmentArenaIndex.ts b/src/hints/dict/getSegmentArenaIndex.ts index 0d94943b..f7311965 100644 --- a/src/hints/dict/getSegmentArenaIndex.ts +++ b/src/hints/dict/getSegmentArenaIndex.ts @@ -27,18 +27,16 @@ export const getSegmentArenaIndexParser = z /** * GetSegmentArenaIndex hint * - * Retrieve the index of the given dict in the info segment. - * Used when finalizing the dictionaries. + * Assert the index of the dictionary to be squashed in memory */ export type GetSegmentArenaIndex = z.infer; /** - * Assert that `dictIndex` address stores the identifier of the - * dictionary found at `dictEndPtr`. - * - * The identifier `id` the is dictionary number, - * n for the n-th dictionary, starting at 0. + * Assert the index of the dictionary to be squashed in memory. * + * @param {VirtualMachine} vm - The virtual machine instance + * @param {ResOperand} dictEndPtr - Pointer to the end of the dictionary. + * @param {CellRef} dictIndex - Address to store the index of the dictionary. */ export const getSegmentArenaIndex = ( vm: VirtualMachine, From 7c7b3d4a561d31ba575fa8ea8dc00ad7c1084c38 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:36:22 +0200 Subject: [PATCH 76/78] doc: improve JSDoc InitSquashData --- src/hints/dict/initSquashData.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/hints/dict/initSquashData.ts b/src/hints/dict/initSquashData.ts index 35a7dcfc..5a698761 100644 --- a/src/hints/dict/initSquashData.ts +++ b/src/hints/dict/initSquashData.ts @@ -49,17 +49,19 @@ export const initSquashDataParser = z /** * InitSquashData hint * - * Initialize the squashed dictionary data. + * Initialize the squashing of a dictionary. */ export type InitSquashData = z.infer; /** - * Initialize the squashed dictionary - * - * - Insert all accessed keys - * - Assert the biggest key to `bigKeys` address - * - Assert the smallest (first) key to `firstKey` address + * Initialize the squashing of a dictionary. * + * @param {VirtualMachine} vm - The virtual machine instance + * @param {ResOperand} dictAccesses - Pointer to the start of the dictionary to be squashed. + * @param {ResOperand} ptrDiff - The difference between dictionary end and start pointers. + * @param {ResOperand} nAccesses - Number of dictonary accesses. + * @param {CellRef} bigKeys - Address to store if the biggest key of the dictionary is above 2 ** 128 or not. + * @param {CellRef} firstKey - Address to store the first key to be squashed. */ export const initSquashData = ( vm: VirtualMachine, From a87335c51deb85526cb720c0fe059fcbfe4764b4 Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:36:32 +0200 Subject: [PATCH 77/78] doc: improve JSDoc ShouldContinueSquashLoop --- src/hints/dict/shouldContinueSquashLoop.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/hints/dict/shouldContinueSquashLoop.ts b/src/hints/dict/shouldContinueSquashLoop.ts index 5adc2191..5a6f9a84 100644 --- a/src/hints/dict/shouldContinueSquashLoop.ts +++ b/src/hints/dict/shouldContinueSquashLoop.ts @@ -14,15 +14,22 @@ export const shouldContinueSquashLoopParser = z shouldContinue: should_continue, })); -/** ShouldContinueSquashLoop hint */ +/** + * ShouldContinueSquashLoop hint + * + * Assert in memory if there are keys that haven't been squashed yet. + */ export type ShouldContinueSquashLoop = z.infer< typeof shouldContinueSquashLoopParser >; /** - * Assert whether the squash loop should be continued. + * Assert in memory if there are keys that haven't been squashed yet. + * + * This is the opposite of `ShouldSkipSquashLoop`. * - * If there is still indices to be squashed, the loop continue. + * @param {VirtualMachine} vm - The virtual machine instance + * @param {CellRef} shouldContinue - Address to store whether the squash loop must continue. */ export const shouldContinueSquashLoop = ( vm: VirtualMachine, From 9ece7de4525ef7faee56b839690a70f227a0a1ca Mon Sep 17 00:00:00 2001 From: Malatrax Date: Tue, 30 Jul 2024 18:36:43 +0200 Subject: [PATCH 78/78] doc: improve JSDoc ShouldSkipSquashLoop --- src/hints/dict/shouldSkipSquashLoop.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/hints/dict/shouldSkipSquashLoop.ts b/src/hints/dict/shouldSkipSquashLoop.ts index 2c5d044b..7d588c37 100644 --- a/src/hints/dict/shouldSkipSquashLoop.ts +++ b/src/hints/dict/shouldSkipSquashLoop.ts @@ -14,13 +14,20 @@ export const shouldSkipSquashLoopParser = z shouldSkipLoop: should_skip_loop, })); -/** ShouldSkipSquashLoop hint */ +/** + * ShouldSkipSquashLoop hint + * + * Assert in memory if there are keys that haven't been squashed yet. + */ export type ShouldSkipSquashLoop = z.infer; /** - * Check whether the squash loop should be skipped. + * Assert in memory if there are keys that haven't been squashed yet. + * + * This is the opposite of `ShouldContinueSquashLoop`. * - * If there is no more indices to be squashed, the loop is skipped. + * @param {VirtualMachine} vm - The virtual machine instance + * @param shouldSkipLoop - Address to store whether the squash loop must be skipped. */ export const shouldSkipSquashLoop = ( vm: VirtualMachine,