Skip to content

Commit

Permalink
fix: add cpp changes
Browse files Browse the repository at this point in the history
  • Loading branch information
IlyasRidhuan committed Jun 14, 2024
1 parent ac54fb0 commit 1821ea5
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 229 deletions.
1 change: 1 addition & 0 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,7 @@ fn handle_black_box_function(avm_instrs: &mut Vec<AvmInstruction>, operation: &B
let num_points = points.size.0;
let scalars_offset = scalars.pointer.0;
// Output array is fixed to 3
assert!(outputs.size == &3u32, "Output array size must be equal to 3");
let outputs_offset = outputs.pointer.0;
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::MSM,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ const std::unordered_map<OpCode, std::vector<OperandType>> OPCODE_WIRE_FORMAT =
OperandType::UINT32, // rhs.y
OperandType::UINT32, // rhs.is_infinite
OperandType::UINT32 } }, // dst_offset
{ OpCode::MSM,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
// Gadget - Conversion
{ OpCode::TORADIXLE,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ enum class OpCode : uint8_t {
SHA256,
PEDERSEN,
ECADD,
MSM,
// Conversions
TORADIXLE,
// Future Gadgets -- pending changes in noir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ contract AvmTest {
fn variable_base_msm() -> [Field; 3] {
let g = EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false };
let scalar = EmbeddedCurveScalar { lo: 3, hi: 0 };
let triple_g = multi_scalar_mul([g], [scalar]);
let scalar2 = EmbeddedCurveScalar { lo: 20, hi: 0 };
let triple_g = multi_scalar_mul([g, g], [scalar, scalar2]);
triple_g
}

Expand Down
12 changes: 8 additions & 4 deletions yarn-project/foundation/src/fields/point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,18 @@ export class Point {

/**
* Check if this is point at infinity.
* Check this is consistent with how bb is encoding the point at infinity
*/
isInfPoint() {
// Check this
return this.x.isZero();
public get inf() {
return this.x == Fr.ZERO;
}
public toFieldsWithInf() {
return [this.x, this.y, new Fr(this.inf)];
}

isOnGrumpkin() {
if (this.isInfPoint()) {
// TODO: Check this against how bb handles curve check and infinity point check
if (this.inf) {
return true;
}

Expand Down
4 changes: 3 additions & 1 deletion yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ describe('AVM simulator: transpiled Noir contracts', () => {
expect(results.reverted).toBe(false);
const grumpkin = new Grumpkin();
const g3 = grumpkin.mul(grumpkin.generator(), new Fq(3));
expect(results.output).toEqual([g3.x, g3.y, Fr.ZERO]);
const g20 = grumpkin.mul(grumpkin.generator(), new Fq(20));
const expectedResult = grumpkin.add(g3, g20);
expect(results.output).toEqual([expectedResult.x, expectedResult.y, Fr.ZERO]);
});

describe('U128 addition and overflows', () => {
Expand Down
244 changes: 116 additions & 128 deletions yarn-project/simulator/src/avm/opcodes/multi_scalar_mul.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,141 +2,129 @@ import { Fq, Fr } from '@aztec/circuits.js';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';

import { type AvmContext } from '../avm_context.js';
import { Field, Uint8, Uint32 } from '../avm_memory_types.js';
import { Field, type MemoryValue, Uint8, Uint32 } from '../avm_memory_types.js';
import { initContext } from '../fixtures/index.js';
import { MultiScalarMul } from './multi_scalar_mul.js';

describe('MultiScalarMul Opcode', () => {
let context: AvmContext;

beforeEach(async () => {
context = initContext();
});
it('Should (de)serialize correctly', () => {
const buf = Buffer.from([
MultiScalarMul.opcode, // opcode
7, // indirect
...Buffer.from('12345678', 'hex'), // pointsOffset
...Buffer.from('23456789', 'hex'), // scalars Offset
...Buffer.from('3456789a', 'hex'), // outputOffset
...Buffer.from('456789ab', 'hex'), // pointsLengthOffset
]);
const inst = new MultiScalarMul(
let context: AvmContext;

beforeEach(async () => {
context = initContext();
});
it('Should (de)serialize correctly', () => {
const buf = Buffer.from([
MultiScalarMul.opcode, // opcode
7, // indirect
...Buffer.from('12345678', 'hex'), // pointsOffset
...Buffer.from('23456789', 'hex'), // scalars Offset
...Buffer.from('3456789a', 'hex'), // outputOffset
...Buffer.from('456789ab', 'hex'), // pointsLengthOffset
]);
const inst = new MultiScalarMul(
/*indirect=*/ 7,
/*pointsOffset=*/ 0x12345678,
/*scalarsOffset=*/ 0x23456789,
/*outputOffset=*/ 0x3456789a,
/*pointsLengthOffset=*/ 0x456789ab,
);

expect(MultiScalarMul.deserialize(buf)).toEqual(inst);
expect(inst.serialize()).toEqual(buf);
});

it('Should perform msm correctly - direct', async () => {
const indirect = 0;
const grumpkin = new Grumpkin();
// We need to ensure points are actually on curve, so we just use the generator
// In future we could use a random point, for now we create an array of [G, 2G, 3G]
const points = Array.from({ length: 3 }, (_, i) => grumpkin.mul(grumpkin.generator(), new Fq(i + 1)));

// Pick some big scalars to test the edge cases
const scalars = [new Fq(Fq.MODULUS - 1n), new Fq(Fq.MODULUS - 2n), new Fq(1n)];
const pointsReadLength = points.length * 3; // multiplied by 3 since we will store them as triplet in avm memory
const scalarsLength = scalars.length * 2; // multiplied by 2 since we will store them as lo and hi limbs in avm memory
// Transform the points and scalars into the format that we will write to memory
// We just store the x and y coordinates here, and handle the infinities when we write to memory
const storedPoints: Field[] = points.flatMap(p => [new Field(p.x), new Field(p.y)]);
const storedScalars: Field[] = scalars.flatMap(s => [new Field(s.low), new Field(s.high)]);

const pointsOffset = 0;
// Store points...awkwardly (This would be simpler if ts handled the infinities in the Point struct)
// Points are stored as [x1, y1, inf1, x2, y2, inf2, ...] where the types are [Field, Field, Uint8, Field, Field, Uint8, ...]
for (let i = 0; i < points.length; i++) {
const flatPointsOffset = pointsOffset + 2 * i; // 2 since we only have x and y
const memoryOffset = pointsOffset + 3 * i; // 3 since we store x, y, inf
context.machineState.memory.set(memoryOffset, storedPoints[flatPointsOffset]);
context.machineState.memory.set(memoryOffset + 1, storedPoints[flatPointsOffset + 1]);
context.machineState.memory.set(memoryOffset + 2, new Uint8(points[i].isInfPoint() ? 1 : 0));
}
// Store scalars
const scalarsOffset = pointsOffset + pointsReadLength;
context.machineState.memory.setSlice(scalarsOffset, storedScalars);
// Store length of points to read
const pointsLengthOffset = scalarsOffset + scalarsLength;
context.machineState.memory.set(pointsLengthOffset, new Uint32(pointsReadLength));
const outputOffset = pointsLengthOffset + 1;

await new MultiScalarMul(indirect, pointsOffset, scalarsOffset, outputOffset, pointsLengthOffset).execute(context);

const result = context.machineState.memory.getSlice(outputOffset, 3);

// We write it out explicitly here
let expectedResult = grumpkin.mul(points[0], scalars[0]);
expectedResult = grumpkin.add(expectedResult, grumpkin.mul(points[1], scalars[1]));
expectedResult = grumpkin.add(expectedResult, grumpkin.mul(points[2], scalars[2]));

expect(result.map(r => r.toFr())).toEqual([expectedResult.x, expectedResult.y, new Fr(0n)]);
});

it('Should perform msm correctly - indirect', async () => {
const indirect = 7;
const grumpkin = new Grumpkin();
// We need to ensure points are actually on curve, so we just use the generator
// In future we could use a random point, for now we create an array of [G, 2G, 3G]
const points = Array.from({ length: 3 }, (_, i) => grumpkin.mul(grumpkin.generator(), new Fq(i + 1)));

// Pick some big scalars to test the edge cases
const scalars = [new Fq(Fq.MODULUS - 1n), new Fq(Fq.MODULUS - 2n), new Fq(1n)];
const pointsReadLength = points.length * 3; // multiplied by 3 since we will store them as triplet in avm memory
const scalarsLength = scalars.length * 2; // multiplied by 2 since we will store them as lo and hi limbs in avm memory
// Transform the points and scalars into the format that we will write to memory
// We just store the x and y coordinates here, and handle the infinities when we write to memory
const storedPoints: Field[] = points.flatMap(p => [new Field(p.x), new Field(p.y)]);
const storedScalars: Field[] = scalars.flatMap(s => [new Field(s.low), new Field(s.high)]);

const pointsOffset = 0;
// Store points...awkwardly (This would be simpler if ts handled the infinities in the Point struct)
// Points are stored as [x1, y1, inf1, x2, y2, inf2, ...] where the types are [Field, Field, Uint8, Field, Field, Uint8, ...]
for (let i = 0; i < points.length; i++) {
const flatPointsOffset = pointsOffset + 2 * i; // 2 since we only have x and y
const memoryOffset = pointsOffset + 3 * i; // 3 since we store x, y, inf
context.machineState.memory.set(memoryOffset, storedPoints[flatPointsOffset]);
context.machineState.memory.set(memoryOffset + 1, storedPoints[flatPointsOffset + 1]);
context.machineState.memory.set(memoryOffset + 2, new Uint8(points[i].isInfPoint() ? 1 : 0));
}
// Store scalars
const scalarsOffset = pointsOffset + pointsReadLength;
context.machineState.memory.setSlice(scalarsOffset, storedScalars);
// Store length of points to read
const pointsLengthOffset = scalarsOffset + scalarsLength;
context.machineState.memory.set(pointsLengthOffset, new Uint32(pointsReadLength));
const outputOffset = pointsLengthOffset + 1;

// Set up the indirect pointers
const pointsIndirectOffset = outputOffset + 3; /* 3 since the output is a triplet */
const scalarsIndirectOffset = pointsIndirectOffset + 1;
const outputIndirectOffset = scalarsIndirectOffset + 1;

context.machineState.memory.set(pointsIndirectOffset, new Uint32(pointsOffset));
context.machineState.memory.set(scalarsIndirectOffset, new Uint32(scalarsOffset));
context.machineState.memory.set(outputIndirectOffset, new Uint32(outputOffset));

await new MultiScalarMul(
indirect,
pointsIndirectOffset,
scalarsIndirectOffset,
outputIndirectOffset,
pointsLengthOffset,
).execute(context);

const result = context.machineState.memory.getSlice(outputOffset, 3);

// We write it out explicitly here
let expectedResult = grumpkin.mul(points[0], scalars[0]);
expectedResult = grumpkin.add(expectedResult, grumpkin.mul(points[1], scalars[1]));
expectedResult = grumpkin.add(expectedResult, grumpkin.mul(points[2], scalars[2]));

expect(result.map(r => r.toFr())).toEqual([expectedResult.x, expectedResult.y, new Fr(0n)]);
});
);

expect(MultiScalarMul.deserialize(buf)).toEqual(inst);
expect(inst.serialize()).toEqual(buf);
});

it('Should perform msm correctly - direct', async () => {
const indirect = 0;
const grumpkin = new Grumpkin();
// We need to ensure points are actually on curve, so we just use the generator
// In future we could use a random point, for now we create an array of [G, 2G, 3G]
const points = Array.from({ length: 3 }, (_, i) => grumpkin.mul(grumpkin.generator(), new Fq(i + 1)));

// Pick some big scalars to test the edge cases
const scalars = [new Fq(Fq.MODULUS - 1n), new Fq(Fq.MODULUS - 2n), new Fq(1n)];
const pointsReadLength = points.length * 3; // multiplied by 3 since we will store them as triplet in avm memory
const scalarsLength = scalars.length * 2; // multiplied by 2 since we will store them as lo and hi limbs in avm memory
// Transform the points and scalars into the format that we will write to memory
// We just store the x and y coordinates here, and handle the infinities when we write to memory
const storedScalars: Field[] = scalars.flatMap(s => [new Field(s.low), new Field(s.high)]);
// Points are stored as [x1, y1, inf1, x2, y2, inf2, ...] where the types are [Field, Field, Uint8, Field, Field, Uint8, ...]
const storedPoints: MemoryValue[] = points
.map(p => p.toFieldsWithInf())
.flatMap(([x, y, inf]) => [new Field(x), new Field(y), new Uint8(inf.toNumber())]);
const pointsOffset = 0;
context.machineState.memory.setSlice(pointsOffset, storedPoints);
// Store scalars
const scalarsOffset = pointsOffset + pointsReadLength;
context.machineState.memory.setSlice(scalarsOffset, storedScalars);
// Store length of points to read
const pointsLengthOffset = scalarsOffset + scalarsLength;
context.machineState.memory.set(pointsLengthOffset, new Uint32(pointsReadLength));
const outputOffset = pointsLengthOffset + 1;

await new MultiScalarMul(indirect, pointsOffset, scalarsOffset, outputOffset, pointsLengthOffset).execute(context);

const result = context.machineState.memory.getSlice(outputOffset, 3).map(r => r.toFr());

// We write it out explicitly here
let expectedResult = grumpkin.mul(points[0], scalars[0]);
expectedResult = grumpkin.add(expectedResult, grumpkin.mul(points[1], scalars[1]));
expectedResult = grumpkin.add(expectedResult, grumpkin.mul(points[2], scalars[2]));

expect(result).toEqual([expectedResult.x, expectedResult.y, new Fr(0n)]);
});

it('Should perform msm correctly - indirect', async () => {
const indirect = 7;
const grumpkin = new Grumpkin();
// We need to ensure points are actually on curve, so we just use the generator
// In future we could use a random point, for now we create an array of [G, 2G, 3G]
const points = Array.from({ length: 3 }, (_, i) => grumpkin.mul(grumpkin.generator(), new Fq(i + 1)));

// Pick some big scalars to test the edge cases
const scalars = [new Fq(Fq.MODULUS - 1n), new Fq(Fq.MODULUS - 2n), new Fq(1n)];
const pointsReadLength = points.length * 3; // multiplied by 3 since we will store them as triplet in avm memory
const scalarsLength = scalars.length * 2; // multiplied by 2 since we will store them as lo and hi limbs in avm memory
// Transform the points and scalars into the format that we will write to memory
// We just store the x and y coordinates here, and handle the infinities when we write to memory
const storedScalars: Field[] = scalars.flatMap(s => [new Field(s.low), new Field(s.high)]);
// Points are stored as [x1, y1, inf1, x2, y2, inf2, ...] where the types are [Field, Field, Uint8, Field, Field, Uint8, ...]
const storedPoints: MemoryValue[] = points
.map(p => p.toFieldsWithInf())
.flatMap(([x, y, inf]) => [new Field(x), new Field(y), new Uint8(inf.toNumber())]);
const pointsOffset = 0;
context.machineState.memory.setSlice(pointsOffset, storedPoints);
// Store scalars
const scalarsOffset = pointsOffset + pointsReadLength;
context.machineState.memory.setSlice(scalarsOffset, storedScalars);
// Store length of points to read
const pointsLengthOffset = scalarsOffset + scalarsLength;
context.machineState.memory.set(pointsLengthOffset, new Uint32(pointsReadLength));
const outputOffset = pointsLengthOffset + 1;

// Set up the indirect pointers
const pointsIndirectOffset = outputOffset + 3; /* 3 since the output is a triplet */
const scalarsIndirectOffset = pointsIndirectOffset + 1;
const outputIndirectOffset = scalarsIndirectOffset + 1;

context.machineState.memory.set(pointsIndirectOffset, new Uint32(pointsOffset));
context.machineState.memory.set(scalarsIndirectOffset, new Uint32(scalarsOffset));
context.machineState.memory.set(outputIndirectOffset, new Uint32(outputOffset));

await new MultiScalarMul(
indirect,
pointsIndirectOffset,
scalarsIndirectOffset,
outputIndirectOffset,
pointsLengthOffset,
).execute(context);

const result = context.machineState.memory.getSlice(outputOffset, 3).map(r => r.toFr());

// We write it out explicitly here
let expectedResult = grumpkin.mul(points[0], scalars[0]);
expectedResult = grumpkin.add(expectedResult, grumpkin.mul(points[1], scalars[1]));
expectedResult = grumpkin.add(expectedResult, grumpkin.mul(points[2], scalars[2]));

expect(result).toEqual([expectedResult.x, expectedResult.y, new Fr(0n)]);
});
});
Loading

0 comments on commit 1821ea5

Please sign in to comment.