Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(avm): pedersen commitment sim #7632

Merged
merged 1 commit into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions avm-transpiler/src/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub enum AvmOpcode {
PEDERSEN, // temp - may be removed, but alot of contracts rely on it
ECADD,
MSM,
PEDERSENCOMMITMENT, // temp
// Conversions
TORADIXLE,
// Other
Expand Down Expand Up @@ -170,6 +171,7 @@ impl AvmOpcode {
AvmOpcode::PEDERSEN => "PEDERSEN",
AvmOpcode::ECADD => "ECADD",
AvmOpcode::MSM => "MSM",
AvmOpcode::PEDERSENCOMMITMENT => "PEDERSENCOMMITMENT",
// Conversions
AvmOpcode::TORADIXLE => "TORADIXLE",
// Other
Expand Down
18 changes: 18 additions & 0 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,24 @@ fn handle_black_box_function(avm_instrs: &mut Vec<AvmInstruction>, operation: &B
..Default::default()
});
}
// Temporary while we dont have efficient noir implementations (again)
BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => {
let input_offset = inputs.pointer.0;
let input_size_offset = inputs.size.0;
let index_offset = domain_separator.0;
let output_offset = output.pointer.0;
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::PEDERSENCOMMITMENT,
indirect: Some(ZEROTH_OPERAND_INDIRECT | FIRST_OPERAND_INDIRECT),
operands: vec![
AvmOperand::U32 { value: input_offset as u32 },
AvmOperand::U32 { value: output_offset as u32 },
AvmOperand::U32 { value: input_size_offset as u32 },
AvmOperand::U32 { value: index_offset 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 @@ -166,10 +166,11 @@ const std::unordered_map<OpCode, std::vector<OperandType>> OPCODE_WIRE_FORMAT =
OperandType::UINT32 } }, // dst_offset
{ OpCode::MSM,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
{ OpCode::PEDERSENCOMMITMENT,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
// Gadget - Conversion
{ OpCode::TORADIXLE,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },

// Gadgets - Unused for now
{ OpCode::SHA256COMPRESSION,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ enum class OpCode : uint8_t {
PEDERSEN,
ECADD,
MSM,
PEDERSENCOMMITMENT,
// Conversions
TORADIXLE,
// Future Gadgets -- pending changes in noir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract AvmTest {
global big_field_136_bits: Field = 0x991234567890abcdef1234567890abcdef;

// Libs
use std::embedded_curve_ops::multi_scalar_mul;
use std::embedded_curve_ops::{multi_scalar_mul, 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 @@ -153,6 +153,12 @@ contract AvmTest {
triple_g
}

#[aztec(public)]
fn pedersen_commit(x: Field, y: Field) -> EmbeddedCurvePoint {
let commitment = dep::std::hash::pedersen_commitment([x, y]);
IlyasRidhuan marked this conversation as resolved.
Show resolved Hide resolved
commitment
}

/************************************************************************
* Misc
************************************************************************/
Expand Down
1 change: 1 addition & 0 deletions yarn-project/simulator/src/avm/avm_gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ const BaseGasCosts: Record<Opcode, Gas> = {
[Opcode.PEDERSEN]: DefaultBaseGasCost,
[Opcode.ECADD]: DefaultBaseGasCost,
[Opcode.MSM]: DefaultBaseGasCost,
[Opcode.PEDERSENCOMMITMENT]: DefaultBaseGasCost,
// Conversions
[Opcode.TORADIXLE]: DefaultBaseGasCost,
// Other
Expand Down
18 changes: 17 additions & 1 deletion yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { computeVarArgsHash } from '@aztec/circuits.js/hash';
import { FunctionSelector } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { keccak256, keccakf1600, pedersenHash, poseidon2Hash, sha256 } from '@aztec/foundation/crypto';
import { keccak256, keccakf1600, pedersenCommit, pedersenHash, poseidon2Hash, sha256 } from '@aztec/foundation/crypto';
import { Fq, Fr } from '@aztec/foundation/fields';
import { type Fieldable } from '@aztec/foundation/serialize';

Expand Down Expand Up @@ -140,6 +140,22 @@ describe('AVM simulator: transpiled Noir contracts', () => {
expect(results.output).toEqual([expectedResult.x, expectedResult.y, Fr.ZERO]);
});

it('pedersen commitment operations', async () => {
const calldata: Fr[] = [new Fr(100), new Fr(1)];
const context = initContext({ env: initExecutionEnvironment({ calldata }) });

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

expect(results.reverted).toBe(false);
// This doesnt include infinites
const expectedResult = pedersenCommit([Buffer.from([100]), Buffer.from([1])]).map(f => new Fr(f));
// TODO: Come back to the handling of infinities when we confirm how they're handled in bb
const isInf = expectedResult[0] === new Fr(0) && expectedResult[1] === new Fr(0);
expectedResult.push(new Fr(isInf));
expect(results.output).toEqual(expectedResult);
});

describe('U128 addition and overflows', () => {
it('U128 addition', async () => {
const calldata: Fr[] = [
Expand Down
93 changes: 93 additions & 0 deletions yarn-project/simulator/src/avm/opcodes/commitment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { pedersenCommit } from '@aztec/foundation/crypto';

import { type AvmContext } from '../avm_context.js';
import { Field, Uint32 } from '../avm_memory_types.js';
import { initContext, randomMemoryFields } from '../fixtures/index.js';
import { Addressing, AddressingMode } from './addressing_mode.js';
import { PedersenCommitment } from './commitment.js';

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

beforeEach(async () => {
context = initContext();
});

describe('Pedersen Commitment', () => {
it('Should (de)serialize correctly', () => {
const buf = Buffer.from([
PedersenCommitment.opcode, // opcode
1, // indirect
...Buffer.from('23456789', 'hex'), // inputOffset
...Buffer.from('3456789a', 'hex'), // inputSizeOffset
...Buffer.from('12345678', 'hex'), // outputOffset
...Buffer.from('00000000', 'hex'), // genIndexOffset
]);
const inst = new PedersenCommitment(
/*indirect=*/ 1,
/*inputOffset=*/ 0x23456789,
/*inputSizeOffset=*/ 0x3456789a,
/*outputOffset=*/ 0x12345678,
/*genIndexOffset=*/ 0,
);

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

it('Should commit correctly - direct', async () => {
const args = randomMemoryFields(10);
const inputOffset = 0;
const inputSizeOffset = 20;
const outputOffset = 50;
const indirect = 0;
const generatorIndexOffset = 10;

context.machineState.memory.setSlice(inputOffset, args);
context.machineState.memory.set(inputSizeOffset, new Uint32(args.length));
context.machineState.memory.set(generatorIndexOffset, new Uint32(0));

const expectedCommitment = pedersenCommit(args.map(f => f.toBuffer())).map(f => new Field(f));
await new PedersenCommitment(indirect, inputOffset, outputOffset, inputSizeOffset, generatorIndexOffset).execute(
context,
);

const result = context.machineState.memory.getSlice(outputOffset, 2);
expect(result).toEqual(expectedCommitment);
// Check Inf
expect(0).toEqual(context.machineState.memory.get(outputOffset + 2).toNumber());
});

it('Should commit correctly - indirect', async () => {
const args = randomMemoryFields(10);
const indirect = new Addressing([
/*inputOffset=*/ AddressingMode.INDIRECT,
/*outputOffset*/ AddressingMode.INDIRECT,
/*inputSizeOffset=*/ AddressingMode.DIRECT,
/*generatorIndexOffset=*/ AddressingMode.DIRECT,
]).toWire();
const inputOffset = 0;
const inputSizeOffset = 20;
const outputOffset = 50;
const realOutputOffset = 100;
const realInputOffset = 200;
const generatorIndexOffset = 51;

context.machineState.memory.set(outputOffset, new Uint32(realOutputOffset));
context.machineState.memory.set(inputOffset, new Uint32(realInputOffset));
context.machineState.memory.setSlice(realInputOffset, args);
context.machineState.memory.set(inputSizeOffset, new Uint32(args.length));
context.machineState.memory.set(generatorIndexOffset, new Uint32(0));

const expectedCommitment = pedersenCommit(args.map(f => f.toBuffer())).map(f => new Field(f));
await new PedersenCommitment(indirect, inputOffset, outputOffset, inputSizeOffset, generatorIndexOffset).execute(
context,
);

const result = context.machineState.memory.getSlice(realOutputOffset, 2);
expect(result).toEqual(expectedCommitment);
// Check Inf
expect(0).toEqual(context.machineState.memory.get(realOutputOffset + 2).toNumber());
});
});
});
66 changes: 66 additions & 0 deletions yarn-project/simulator/src/avm/opcodes/commitment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { pedersenCommit } from '@aztec/foundation/crypto';

import { type AvmContext } from '../avm_context.js';
import { Field, TypeTag, Uint8 } from '../avm_memory_types.js';
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
import { Addressing } from './addressing_mode.js';
import { Instruction } from './instruction.js';

export class PedersenCommitment extends Instruction {
static type: string = 'PEDERSENCOMMITMENT';
static readonly opcode: Opcode = Opcode.PEDERSENCOMMITMENT;

// Informs (de)serialization. See Instruction.deserialize.
static readonly wireFormat: OperandType[] = [
OperandType.UINT8 /* Opcode */,
OperandType.UINT8 /* Indirect */,
OperandType.UINT32 /* Input Offset*/,
OperandType.UINT32 /* Dst Offset */,
OperandType.UINT32 /* Input Size Offset */,
OperandType.UINT32 /* Generator Index Offset */,
];

constructor(
private indirect: number,
private inputOffset: number,
private outputOffset: number,
private inputSizeOffset: number,
private genIndexOffset: number,
) {
super();
}

public async execute(context: AvmContext): Promise<void> {
const memory = context.machineState.memory.track(this.type);
const [inputOffset, outputOffset, inputSizeOffset, genIndexOffset] = Addressing.fromWire(this.indirect).resolve(
[this.inputOffset, this.outputOffset, this.inputSizeOffset, this.genIndexOffset],
memory,
);

const inputSize = memory.get(inputSizeOffset).toNumber();
memory.checkTag(TypeTag.UINT32, inputSizeOffset);

const inputs = memory.getSlice(inputOffset, inputSize);
memory.checkTagsRange(TypeTag.FIELD, inputOffset, inputSize);

// Generator index not used for now since we dont utilise it in the pedersenCommit function
IlyasRidhuan marked this conversation as resolved.
Show resolved Hide resolved
memory.checkTag(TypeTag.UINT32, genIndexOffset);

const memoryOperations = { reads: inputSize + 1, writes: 3, indirect: this.indirect };
context.machineState.consumeGas(this.gasCost(memoryOperations));

const inputBuffer: Buffer[] = inputs.map(input => input.toBuffer());
// TODO: Add the generate index to the pedersenCommit function
const commitment = pedersenCommit(inputBuffer).map(f => new Field(f));
// The function doesnt include a flag if the output point is infinity, come back to this
// for now we just check if theyre zero - until we know how bb encodes them
const isInfinity = commitment[0].equals(new Field(0)) && commitment[1].equals(new Field(0));

memory.set(outputOffset, commitment[0]); // Field typed
memory.set(outputOffset + 1, commitment[1]); // Field typed
memory.set(outputOffset + 2, new Uint8(isInfinity ? 1 : 0)); // U8 typed

memory.assert(memoryOperations);
context.machineState.incrementPc();
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PedersenCommitment } from '../opcodes/commitment.js';
import { DAGasLeft, L2GasLeft } from '../opcodes/context_getters.js';
import { EcAdd } from '../opcodes/ec_add.js';
import { Keccak, KeccakF1600, Pedersen, Poseidon2, Sha256 } from '../opcodes/hashing.js';
Expand Down Expand Up @@ -146,6 +147,7 @@ const INSTRUCTION_SET = () =>
[Sha256.opcode, Sha256],
[Pedersen.opcode, Pedersen],
[MultiScalarMul.opcode, MultiScalarMul],
[PedersenCommitment.opcode, PedersenCommitment],
// Conversions
[ToRadixLE.opcode, ToRadixLE],
// Future Gadgets -- pending changes in noir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export enum Opcode {
PEDERSEN, // temp - may be removed, but alot of contracts rely on it
ECADD,
MSM,
PEDERSENCOMMITMENT,
// Conversion
TORADIXLE,
// Future Gadgets -- pending changes in noir
Expand Down
Loading