Skip to content

Commit

Permalink
refactor: use named constants for dict and segment arena related offs…
Browse files Browse the repository at this point in the history
…et in hints
  • Loading branch information
zmalatrax committed Jul 30, 2024
1 parent 80e05ca commit c489739
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 53 deletions.
27 changes: 27 additions & 0 deletions src/builtins/segmentArena.ts
Original file line number Diff line number Diff line change
@@ -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 = {};
10 changes: 10 additions & 0 deletions src/errors/dictionary.ts
Original file line number Diff line number Diff line change
@@ -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()}`);
}
}
7 changes: 0 additions & 7 deletions src/errors/virtualMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()}`);
}
}
8 changes: 5 additions & 3 deletions src/hints/dict/allocFelt252Dict.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion src/hints/dict/felt252DictEntryInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
};
5 changes: 3 additions & 2 deletions src/hints/dict/felt252DictEntryUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/hints/dict/initSquashData.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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
Expand Down
28 changes: 28 additions & 0 deletions src/hints/dictionary.test.ts
Original file line number Diff line number Diff line change
@@ -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));
});
});
27 changes: 27 additions & 0 deletions src/hints/dictionary.ts
Original file line number Diff line number Diff line change
@@ -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<string, SegmentValue> {
constructor(public readonly id: Felt) {
super();
this.id = id;
}
}
28 changes: 2 additions & 26 deletions src/vm/virtualMachine.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 = {
Expand Down Expand Up @@ -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));
});
});
});
17 changes: 5 additions & 12 deletions src/vm/virtualMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -61,15 +63,6 @@ export type RelocatedMemory = {
value: Felt;
};

export class Dictionary extends Map<string, SegmentValue> {
constructor(public readonly id: Felt) {
super();
this.id = id;
}
}

export const DICT_ACCESS_SIZE = 3;

export class VirtualMachine {
private currentStep: bigint;
memory: Memory;
Expand Down

0 comments on commit c489739

Please sign in to comment.