Skip to content

Commit

Permalink
plumb through transpiler
Browse files Browse the repository at this point in the history
use barretenberg wasm for ec ops
  • Loading branch information
just-mitch authored and IlyasRidhuan committed Jun 5, 2024
1 parent a6d8289 commit 515ec55
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 141 deletions.
2 changes: 2 additions & 0 deletions avm-transpiler/src/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub enum AvmOpcode {
POSEIDON2,
SHA256, // temp - may be removed, but alot of contracts rely on it
PEDERSEN, // temp - may be removed, but alot of contracts rely on it
ECADD,
// Conversions
TORADIXLE,
}
Expand Down Expand Up @@ -163,6 +164,7 @@ impl AvmOpcode {
AvmOpcode::POSEIDON2 => "POSEIDON2",
AvmOpcode::SHA256 => "SHA256 ",
AvmOpcode::PEDERSEN => "PEDERSEN",
AvmOpcode::ECADD => "ECADD",
// Conversions
AvmOpcode::TORADIXLE => "TORADIXLE",
}
Expand Down
22 changes: 22 additions & 0 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,28 @@ fn handle_black_box_function(avm_instrs: &mut Vec<AvmInstruction>, operation: &B
],
});
}
BlackBoxOp::EmbeddedCurveAdd {
input1_x,
input1_y,
input1_infinite,
input2_x,
input2_y,
input2_infinite,
result,
} => avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::ECADD,
indirect: Some(ALL_DIRECT),
operands: vec![
AvmOperand::U32 { value: input1_x.0 as u32 },
AvmOperand::U32 { value: input1_y.0 as u32 },
AvmOperand::U8 { value: input1_infinite.0 as u8 },
AvmOperand::U32 { value: input2_x.0 as u32 },
AvmOperand::U32 { value: input2_y.0 as u32 },
AvmOperand::U8 { value: input2_infinite.0 as u8 },
AvmOperand::U32 { value: result.pointer.0 as u32 },
],
..Default::default()
}),
_ => panic!("Transpiler doesn't know how to process {:?}", operation),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ contract AvmTest {
global big_field_136_bits: Field = 0x991234567890abcdef1234567890abcdef;

// Libs
use dep::std::embedded_curve_ops::EmbeddedCurvePoint;
use dep::aztec::protocol_types::constants::CONTRACT_INSTANCE_LENGTH;
use dep::aztec::prelude::{Map, Deserialize};
use dep::aztec::state_vars::PublicMutable;
Expand Down Expand Up @@ -133,6 +134,15 @@ contract AvmTest {
a % 2
}

#[aztec(public)]
fn elliptic_curve_add_and_double() -> EmbeddedCurvePoint {
let g = EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false };

let doubled = g + g;
let added = g + doubled;
added
}

/************************************************************************
* Misc
************************************************************************/
Expand Down
27 changes: 0 additions & 27 deletions yarn-project/foundation/src/fields/point.test.ts

This file was deleted.

76 changes: 2 additions & 74 deletions yarn-project/foundation/src/fields/point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { Fr } from './fields.js';
export class Point {
static ZERO = new Point(Fr.ZERO, Fr.ZERO);
static SIZE_IN_BYTES = Fr.SIZE_IN_BYTES * 2;
static G = new Point(new Fr(1n), new Fr(17631683881184975370165255887551781615748388533673675138860n));

/** Used to differentiate this class from AztecAddress */
public readonly kind = 'point';
Expand Down Expand Up @@ -47,7 +46,7 @@ export class Point {
*/
static fromBuffer(buffer: Buffer | BufferReader) {
const reader = BufferReader.asReader(buffer);
return new this(Fr.fromBuffer(reader.readBytes(32)), Fr.fromBuffer(reader.readBytes(32)));
return new this(Fr.fromBuffer(reader), Fr.fromBuffer(reader));
}

/**
Expand Down Expand Up @@ -138,8 +137,7 @@ export class Point {
return poseidon2Hash(this.toFields());
}

is_on_grumpkin() {
// allow zero point to represent infinity
isOnGrumpkin() {
if (this.isZero()) {
return true;
}
Expand All @@ -150,76 +148,6 @@ export class Point {
const rhs = this.x.square().mul(this.x).sub(A);
return lhs.equals(rhs);
}

/**
* Double the current Point instance.
* Returns a new Point instance representing the result of the doubling operation.
*
* @returns A new Point instance representing the result of the doubling operation.
*/
double() {
if (this.isZero()) {
return this;
}

const two = new Fr(2);
const three = new Fr(3);

const lambda = this.x.square().mul(three).div(this.y.mul(two));
const x = lambda.square().sub(this.x.mul(two));
const y = lambda.mul(this.x.sub(x)).sub(this.y);
return new Point(x, y);
}

/**
* Add another Point instance to the current instance.
* Returns a new Point instance representing the sum of the two points.
*
* @param rhs - The Point instance to add to the current instance.
* @returns A new Point instance representing the sum of the two points.
*/
add(rhs: Point) {
if (this.isZero()) {
return rhs;
}
if (rhs.isZero()) {
return this;
}

if (this.equals(rhs)) {
return this.double();
}

if (this.x.equals(rhs.x) && this.y.equals(rhs.y.negate())) {
return Point.ZERO;
}

const lambda = this.y.sub(rhs.y).div(this.x.sub(rhs.x));
const x = lambda.square().sub(this.x).sub(rhs.x);
const y = lambda.mul(this.x.sub(x)).sub(this.y);
return new Point(x, y);
}

/**
* Negate the current Point instance.
* Returns a new Point instance representing the negation of the current instance.
*
* @returns A new Point instance representing the negation of the current instance.
*/
negate() {
return new Point(this.x, this.y.negate());
}

/**
* Subtract another Point instance from the current instance.
* Returns a new Point instance representing the difference of the two points.
*
* @param rhs - The Point instance to subtract from the current instance.
* @returns A new Point instance representing the difference of the two points.
*/
sub(rhs: Point) {
return this.add(rhs.negate());
}
}

/**
Expand Down
16 changes: 15 additions & 1 deletion yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { UnencryptedL2Log } from '@aztec/circuit-types';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { computeVarArgsHash } from '@aztec/circuits.js/hash';
import { EventSelector, FunctionSelector } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { keccak256, pedersenHash, poseidon2Hash, sha256 } from '@aztec/foundation/crypto';
import { Fr } from '@aztec/foundation/fields';
import { Fq, Fr } from '@aztec/foundation/fields';
import { type Fieldable } from '@aztec/foundation/serialize';

import { jest } from '@jest/globals';
Expand Down Expand Up @@ -96,6 +97,19 @@ describe('AVM simulator: transpiled Noir contracts', () => {
expect(await isAvmBytecode(bytecode));
});

it('elliptic curve operations', async () => {
const calldata: Fr[] = [];
const context = initContext({ env: initExecutionEnvironment({ calldata }) });

const bytecode = getAvmTestContractBytecode('elliptic_curve_add_and_double');
const results = await new AvmSimulator(context).executeBytecode(bytecode);

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]);
});

describe('U128 addition and overflows', () => {
it('U128 addition', async () => {
const calldata: Fr[] = [
Expand Down
85 changes: 62 additions & 23 deletions yarn-project/simulator/src/avm/opcodes/ec_add.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Point } from '@aztec/circuits.js';
import { Fr, Point } from '@aztec/circuits.js';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';

import { beforeEach } from '@jest/globals';

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

describe('EC Instructions', () => {
let context: AvmContext;
const grumpkin: Grumpkin = new Grumpkin();

beforeEach(() => {
context = initContext();
Expand All @@ -19,55 +21,92 @@ describe('EC Instructions', () => {
const buf = Buffer.from([
EcAdd.opcode, // opcode
0x01, // indirect
...Buffer.from('12345678', 'hex'), // aOffset
...Buffer.from('23456789', 'hex'), // bOffset
...Buffer.from('3456789a', 'hex'), // dstOffset
...Buffer.from('12345670', 'hex'), // p1x
...Buffer.from('12345671', 'hex'), // p1y
...Buffer.from('00', 'hex'), // p1IsInfinite
...Buffer.from('12345672', 'hex'), // p2x
...Buffer.from('12345673', 'hex'), // p2y
...Buffer.from('01', 'hex'), // p2IsInfinite
...Buffer.from('12345674', 'hex'), // dstOffset
]);
const inst = new EcAdd(
/*indirect=*/ 0x01,
/*aOffset=*/ 0x12345678,
/*bOffset=*/ 0x23456789,
/*dstOffset=*/ 0x3456789a,
/*p1X=*/ 0x12345670,
/*p1Y=*/ 0x12345671,
/*p1IsInfinite=*/ 0,
/*p2X=*/ 0x12345672,
/*p2Y=*/ 0x12345673,
/*p2IsInfinite=*/ 1,
/*dstOffset=*/ 0x12345674,
);

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

it(`Should double correctly`, async () => {
const x = new Field(Point.G.x);
const y = new Field(Point.G.y);
const x = new Field(grumpkin.generator().x);
const y = new Field(grumpkin.generator().y);
const zero = new Uint8(0);

context.machineState.memory.set(0, x);
context.machineState.memory.set(1, y);
context.machineState.memory.set(2, x);
context.machineState.memory.set(3, y);
context.machineState.memory.set(2, zero);
context.machineState.memory.set(3, x);
context.machineState.memory.set(4, y);
context.machineState.memory.set(5, zero);
context.machineState.memory.set(6, new Uint32(6));

await new EcAdd(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 4).execute(context);
await new EcAdd(
/*indirect=*/ 0,
/*p1X=*/ 0,
/*p1Y=*/ 1,
/*p1IsInfinite=*/ 2,
/*p2X=*/ 3,
/*p2Y=*/ 4,
/*p2IsInfinite=*/ 5,
/*dstOffset=*/ 6,
).execute(context);

const actual = new Point(context.machineState.memory.get(4).toFr(), context.machineState.memory.get(5).toFr());
const expected = Point.G.double();
const actual = new Point(context.machineState.memory.get(6).toFr(), context.machineState.memory.get(7).toFr());
const expected = grumpkin.add(grumpkin.generator(), grumpkin.generator());
expect(actual).toEqual(expected);
expect(context.machineState.memory.get(8).toFr().equals(Fr.ZERO)).toBe(true);
});

it('Should add correctly', async () => {
const G2 = Point.G.add(Point.G);
console.log(grumpkin.generator());
const G2 = grumpkin.add(grumpkin.generator(), grumpkin.generator());
const zero = new Uint8(0);

const x1 = new Field(Point.G.x);
const y1 = new Field(Point.G.y);
const x1 = new Field(grumpkin.generator().x);
const y1 = new Field(grumpkin.generator().y);
const x2 = new Field(G2.x);
const y2 = new Field(G2.y);

context.machineState.memory.set(0, x1);
context.machineState.memory.set(1, y1);
context.machineState.memory.set(2, x2);
context.machineState.memory.set(3, y2);
context.machineState.memory.set(2, zero);
context.machineState.memory.set(3, x2);
context.machineState.memory.set(4, y2);
context.machineState.memory.set(5, zero);
context.machineState.memory.set(6, new Uint32(6));

await new EcAdd(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 4).execute(context);
await new EcAdd(
/*indirect=*/ 0,
/*p1X=*/ 0,
/*p1Y=*/ 1,
/*p1IsInfinite=*/ 2,
/*p2X=*/ 3,
/*p2Y=*/ 4,
/*p2IsInfinite=*/ 5,
/*dstOffset=*/ 6,
).execute(context);

const actual = new Point(context.machineState.memory.get(4).toFr(), context.machineState.memory.get(5).toFr());
const G3 = G2.add(Point.G);
const actual = new Point(context.machineState.memory.get(6).toFr(), context.machineState.memory.get(7).toFr());
const G3 = grumpkin.add(grumpkin.generator(), G2);
expect(actual).toEqual(G3);
expect(context.machineState.memory.get(8).toFr().equals(Fr.ZERO)).toBe(true);
});
});
});
Loading

0 comments on commit 515ec55

Please sign in to comment.