Skip to content

Commit

Permalink
feat: add Pedersen builtin
Browse files Browse the repository at this point in the history
  • Loading branch information
zmalatrax committed May 31, 2024
1 parent ae2743b commit edf1052
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 0 deletions.
27 changes: 27 additions & 0 deletions cairo_programs/cairo_0/pedersen_builtin.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
%builtins pedersen

from starkware.cairo.common.cairo_builtins import HashBuiltin

func main{pedersen_ptr: HashBuiltin*}() {
assert pedersen_ptr.x = 0;
assert pedersen_ptr.y = 0;
assert pedersen_ptr.result = 0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804;
let pedersen_ptr = pedersen_ptr + HashBuiltin.SIZE;

assert pedersen_ptr.x = 0;
assert pedersen_ptr.y = 1;
assert pedersen_ptr.result = 0x46c9aeb066cc2f41c7124af30514f9e607137fbac950524f5fdace5788f9d43;
let pedersen_ptr = pedersen_ptr + HashBuiltin.SIZE;

assert pedersen_ptr.x = 1;
assert pedersen_ptr.y = 0;
assert pedersen_ptr.result = 0x268a9d47dde48af4b6e2c33932ed1c13adec25555abaa837c376af4ea2f8a94;
let pedersen_ptr = pedersen_ptr + HashBuiltin.SIZE;

assert pedersen_ptr.x = 54;
assert pedersen_ptr.y = 1249832432;
assert pedersen_ptr.result = 0x20120a7d08fd21654c72a9281841406543b16d00faaca1069332053c41c07b8;
let pedersen_ptr = pedersen_ptr + HashBuiltin.SIZE;

return ();
}
2 changes: 2 additions & 0 deletions src/builtins/builtin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SegmentValue } from 'primitives/segmentValue';
import { bitwiseHandler } from './bitwise';
import { ecOpHandler } from './ecop';
import { pedersenHandler } from './pedersen';

/** Proxy handler to abstract validation & deduction rules off the VM */
export type BuiltinHandler = ProxyHandler<Array<SegmentValue>>;
Expand All @@ -22,6 +23,7 @@ const BUILTIN_HANDLER: {
} = {
bitwise: bitwiseHandler,
ec_op: ecOpHandler,
pedersen: pedersenHandler,
};

/** Getter of the object `BUILTIN_HANDLER` */
Expand Down
94 changes: 94 additions & 0 deletions src/builtins/pedersen.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { describe, expect, test } from 'bun:test';

import { UndefinedValue } from 'errors/builtins';

import { Felt } from 'primitives/felt';
import { Relocatable } from 'primitives/relocatable';
import { Memory } from 'memory/memory';
import { bitwiseHandler } from './bitwise';
import { ExpectedFelt } from 'errors/virtualMachine';
import { pedersenHandler } from './pedersen';

type PedersenInput = {
x: Felt;
y: Felt;
expected: Felt;
};

describe('Pedersen', () => {
const inputs: PedersenInput[] = [
{
x: new Felt(0n),
y: new Felt(0n),
expected: new Felt(
0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804n
),
},
{
x: new Felt(0n),
y: new Felt(1n),
expected: new Felt(
0x46c9aeb066cc2f41c7124af30514f9e607137fbac950524f5fdace5788f9d43n
),
},
{
x: new Felt(1n),
y: new Felt(0n),
expected: new Felt(
0x268a9d47dde48af4b6e2c33932ed1c13adec25555abaa837c376af4ea2f8a94n
),
},
{
x: new Felt(54n),
y: new Felt(1249832432n),
expected: new Felt(
0x20120a7d08fd21654c72a9281841406543b16d00faaca1069332053c41c07b8n
),
},
];

test.each(inputs)(
'should properly compute pedersen hash',
({ x, y, expected }: PedersenInput) => {
const memory = new Memory();
const { segmentId } = memory.addSegment(pedersenHandler);
const addressHash = new Relocatable(segmentId, 2);

memory.assertEq(new Relocatable(segmentId, 0), x);
memory.assertEq(new Relocatable(segmentId, 1), y);

expect(memory.get(addressHash)).toEqual(expected);
}
);

test.each([0, 1])(
'should throw UndefinedValue error when one of the two input is not constrained',
(offset: number) => {
const memory = new Memory();
const { segmentId } = memory.addSegment(bitwiseHandler);
const address = new Relocatable(segmentId, offset);
const addressHash = new Relocatable(segmentId, 2);

memory.assertEq(address, new Felt(0n));

expect(() => memory.get(addressHash)).toThrow(
new UndefinedValue((offset + 1) % 2)
);
}
);

test('should throw ExpectedFelt error when trying to constrain an input cell to a Relocatable', () => {
const memory = new Memory();
const { segmentId } = memory.addSegment(bitwiseHandler);
const addressHash = new Relocatable(segmentId, 2);

const xAddr = new Relocatable(segmentId, 0);
const yAddr = new Relocatable(segmentId, 1);
const y = new Felt(1n);

memory.assertEq(xAddr, xAddr);
memory.assertEq(yAddr, y);

expect(() => memory.get(addressHash)).toThrow(new ExpectedFelt());
});
});
38 changes: 38 additions & 0 deletions src/builtins/pedersen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { UndefinedValue } from 'errors/builtins';
import { BuiltinHandler } from './builtin';
import { ExpectedFelt } from 'errors/virtualMachine';
import { isFelt } from 'primitives/segmentValue';
import { pedersen } from '@scure/starknet';
import { Felt } from 'primitives/felt';

/** Pedersen Builtin - Computes Pedersen(x, y) */
export const pedersenHandler: BuiltinHandler = {
get(target, prop) {
if (isNaN(Number(prop))) {
return Reflect.get(target, prop);
}

const cellsPerPedersen = 3;
const inputCellsPerPedersen = 2;

const offset = Number(prop);
const pedersenIndex = offset % cellsPerPedersen;
if (pedersenIndex < inputCellsPerPedersen) {
return target[offset];
}

const xOffset = offset - pedersenIndex;
const xValue = target[xOffset];
if (!xValue) throw new UndefinedValue(xOffset);
if (!isFelt(xValue)) throw new ExpectedFelt();
const x = xValue.toBigInt();

const yOffset = xOffset + 1;
const yValue = target[yOffset];
if (!yValue) throw new UndefinedValue(yOffset);
if (!isFelt(yValue)) throw new ExpectedFelt();
const y = yValue.toBigInt();

return (target[offset] = new Felt(BigInt(pedersen(x, y))));
},
};
38 changes: 38 additions & 0 deletions src/runners/cairoRunner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ const EC_OP_PROGRAM_STRING = fs.readFileSync(
'utf8'
);

const PEDERSEN_PROGRAM_STRING = fs.readFileSync(
'cairo_programs/cairo_0/pedersen_builtin.json',
'utf8'
);

const FIBONACCI_PROGRAM = parseProgram(FIBONACCI_PROGRAM_STRING);
const BITWISE_PROGRAM = parseProgram(BITWISE_PROGRAM_STRING);
const EC_OP_PROGRAM = parseProgram(EC_OP_PROGRAM_STRING);
const PEDERSEN_PROGRAM = parseProgram(PEDERSEN_PROGRAM_STRING);

describe('cairoRunner', () => {
describe('constructor', () => {
Expand Down Expand Up @@ -134,5 +140,37 @@ describe('cairoRunner', () => {
expect(runner.vm.memory.get(executionEnd.sub(2))).toEqual(expectedRy);
});
});

describe('pedersen', () => {
test('should properly compute Pedersen hashes of (0, 0), (0, 1), (1, 0) and (54, 1249832432) tuples', () => {
const runner = new CairoRunner(PEDERSEN_PROGRAM);
const config: RunOptions = {
relocate: true,
relocateOffset: 1,
};
runner.run(config);

const expectedHashes = [
0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804n,
0x46c9aeb066cc2f41c7124af30514f9e607137fbac950524f5fdace5788f9d43n,
0x268a9d47dde48af4b6e2c33932ed1c13adec25555abaa837c376af4ea2f8a94n,
0x20120a7d08fd21654c72a9281841406543b16d00faaca1069332053c41c07b8n,
].map((value) => new Felt(value));

const executionSize = runner.vm.memory.getSegmentSize(1);
const executionEnd = runner.executionBase.add(executionSize);

const cellsPerPedersen = 3;
const start = expectedHashes.length * cellsPerPedersen - 1;

expectedHashes.forEach((hash, index) =>
expect(
runner.vm.memory.get(
executionEnd.sub(start - cellsPerPedersen * index)
)
).toEqual(hash)
);
});
});
});
});

0 comments on commit edf1052

Please sign in to comment.