From 72a499f5bef8378717630d2fd80ac84007d3f11b Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 9 Feb 2024 01:42:34 +0000 Subject: [PATCH 1/9] feat(avm): hashing to simulator --- avm-transpiler/src/transpile.rs | 35 ++++++++++++++++++- .../contracts/avm_test_contract/src/main.nr | 10 ++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index ad079b4b098c..8843b0fb8398 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -1,7 +1,7 @@ use acvm::acir::brillig::Opcode as BrilligOpcode; use acvm::acir::circuit::brillig::Brillig; -use acvm::brillig_vm::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, Value, ValueOrArray}; +use acvm::brillig_vm::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, MemoryAddress, Value, ValueOrArray}; use crate::instructions::{ AvmInstruction, AvmOperand, AvmTypeTag, ALL_DIRECT, FIRST_OPERAND_INDIRECT, @@ -203,6 +203,7 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec { BrilligOpcode::ForeignCall { function, destinations, inputs, destination_value_types:_, input_value_types:_ } => { handle_foreign_call(&mut avm_instrs, function, destinations, inputs); }, + BrilligOpcode::BlackBox(operation) => handle_black_box_function(&mut avm_instrs, operation), _ => panic!( "Transpiler doesn't know how to process {:?} brillig instruction", brillig_instr @@ -351,6 +352,38 @@ fn emit_mov(indirect: Option, source: u32, dest: u32) -> AvmInstruction { } } +fn handle_black_box_function(avm_instrs: &mut Vec, operation: &BlackBoxOp) { + match operation { + BlackBoxOp::Keccak256 { message, output } => { + // For the meantime, we output keccak as 32 u8s + let hash_offset = message.pointer.0; + let hash_size = message.size.0; + + let out_offset = output.pointer.0; + let out_size = output.size; + assert!(out_size == 32); + + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::KECCAK, + operands: vec![AvmOperand::U32 { + value: hash_offset as u32, + }, + AvmOperand::U32 { + value: hash_size as u32, + }, + AvmOperand::U32 { + value: out_offset as u32, + }], + ..Default::default() + }); + }, + _ => panic!( + "Transpiler doesn't know how to process BlackBoxOp {:?}", + operation + ), + } +} + /// Compute an array that maps each Brillig pc to an AVM pc. /// This must be done before transpiling to properly transpile jump destinations. /// This is necessary for two reasons: diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 64fe1644489f..ef7e43d152f3 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -51,6 +51,16 @@ contract AvmTest { 200 as Field } + /************************************************************************ + * Hashing functions + ************************************************************************/ + #[aztec(public-vm)] + fn oneFieldKeccak(data: [u8; 32]) -> pub Field { + // let data_as_bytes = data.to_be_bytes(32); + let output = dep::std::hash::keccak256(data, 32); + output[0] as Field + } + /************************************************************************ * AvmContext functions ************************************************************************/ From 4140bd2a38531e4528baa665aa40a260ca621240 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 14 Feb 2024 00:00:28 +0000 Subject: [PATCH 2/9] fix --- avm-transpiler/src/transpile.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 8843b0fb8398..d455e1c9d623 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -93,7 +93,6 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec { value: destination.to_usize() as u32, }, ], - ..Default::default() }); } BrilligOpcode::CalldataCopy { destination_address, size, offset } => { @@ -231,7 +230,7 @@ fn handle_foreign_call( inputs: &Vec, ) { // For the foreign calls we want to handle, we do not want inputs, as they are getters - assert!(inputs.len() == 0); + assert!(inputs.is_empty()); assert!(destinations.len() == 1); let dest_offset_maybe = destinations[0]; let dest_offset = match dest_offset_maybe { @@ -317,7 +316,7 @@ fn emit_set(tag: AvmTypeTag, dest: u32, value: u128) -> AvmInstruction { AvmTypeTag::UINT64 => AvmOperand::U64 { value: value as u64, }, - AvmTypeTag::UINT128 => AvmOperand::U128 { value: value }, + AvmTypeTag::UINT128 => AvmOperand::U128 { value }, _ => panic!("Invalid type tag {:?} for set", tag), }, // dest offset @@ -343,7 +342,7 @@ fn emit_cast(source: u32, destination: u32, dst_tag: AvmTypeTag) -> AvmInstructi fn emit_mov(indirect: Option, source: u32, dest: u32) -> AvmInstruction { AvmInstruction { opcode: AvmOpcode::MOV, - indirect: indirect, + indirect, operands: vec![ AvmOperand::U32 { value: source }, AvmOperand::U32 { value: dest }, @@ -402,10 +401,7 @@ fn map_brillig_pcs_to_avm_pcs(initial_offset: usize, brillig: &Brillig) -> Vec 2, BrilligOpcode::Store { .. } => 2, - BrilligOpcode::Const { bit_size, .. } => match bit_size { - 254 => 2, // Field. - _ => 1, - }, + BrilligOpcode::Const { bit_size: 254, .. } => 2 , _ => 1, }; // next Brillig pc will map to an AVM pc offset by the From 9acb8ffd4e82b266693c209a366b1b858b44091c Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 14 Feb 2024 00:22:15 +0000 Subject: [PATCH 3/9] chore: cast instr --- avm-transpiler/src/transpile.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index d455e1c9d623..b62ba619ec98 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -199,6 +199,9 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec { ..Default::default() }); }, + BrilligOpcode::Cast { destination, source, bit_size } => { + avm_instrs.push(emit_cast(source.to_usize() as u32, destination.to_usize() as u32, tag_from_bit_size(*bit_size))); + } BrilligOpcode::ForeignCall { function, destinations, inputs, destination_value_types:_, input_value_types:_ } => { handle_foreign_call(&mut avm_instrs, function, destinations, inputs); }, From 352de03b3603077f59e78bb25d12c6a092805407 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 14 Feb 2024 17:58:22 +0000 Subject: [PATCH 4/9] temp --- avm-transpiler/src/transpile.rs | 139 +++++++++++++++--- noir-projects/aztec-nr/aztec/src/avm.nr | 1 + noir-projects/aztec-nr/aztec/src/avm/hash.nr | 8 + .../contracts/avm_test_contract/src/main.nr | 28 +++- .../simulator/src/avm/avm_simulator.test.ts | 52 +++++++ .../simulator/src/avm/avm_simulator.ts | 1 + .../simulator/src/avm/opcodes/hashing.ts | 13 +- .../simulator/src/avm/opcodes/memory.ts | 1 + 8 files changed, 214 insertions(+), 29 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/avm/hash.nr diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index b62ba619ec98..0581a7e926c1 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -1,7 +1,7 @@ use acvm::acir::brillig::Opcode as BrilligOpcode; use acvm::acir::circuit::brillig::Brillig; -use acvm::brillig_vm::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, MemoryAddress, Value, ValueOrArray}; +use acvm::brillig_vm::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, MemoryAddress, Value, ValueOrArray}; use crate::instructions::{ AvmInstruction, AvmOperand, AvmTypeTag, ALL_DIRECT, FIRST_OPERAND_INDIRECT, @@ -231,6 +231,109 @@ fn handle_foreign_call( function: &String, destinations: &Vec, inputs: &Vec, +) { + match function.as_str() { + "keccak256" | "sha256" => handle_2_field_hash_instruction(avm_instrs, function, destinations, inputs), + "poseidon" => handle_field_hash_instruction(avm_instrs, function, destinations, inputs), + _ => handle_getter_instruction(avm_instrs, function, destinations, inputs) + } +} + +fn handle_2_field_hash_instruction( + avm_instrs: &mut Vec, + function: &String, + destinations: &Vec, + inputs: &Vec, +) { + // handle field returns differently + let hash_offset_maybe = inputs[0]; + println!("hash_offset_maybe: {:?}", hash_offset_maybe); + let (hash_offset, hash_size) = match hash_offset_maybe { + ValueOrArray::HeapArray(HeapArray { pointer, size}) => (pointer.0, size), + _ => panic!("Keccak | Sha256 address inputs destination should be a single value"), + }; + + assert!(destinations.len() == 1); + let dest_offset_maybe = destinations[0]; + let dest_offset = match dest_offset_maybe { + ValueOrArray::HeapArray(HeapArray { pointer, size}) => { + assert!(size == 2); + pointer.0 + }, + _ => panic!("Keccak | Poseidon address destination should be a single value"), + }; + + let opcode = match function.as_str() { + "keccak256" => AvmOpcode::KECCAK, + "sha256" => AvmOpcode::SHA256, + _ => panic!("Transpiler doesn't know how to process ForeignCall function {:?}", function), + }; + + + avm_instrs.push(AvmInstruction { + opcode, + operands: vec![AvmOperand::U32 { + value: dest_offset as u32, + }, + AvmOperand::U32 { + value: hash_offset as u32, + }, + AvmOperand::U32 { + value: hash_size as u32, + }], + ..Default::default() + }); + +} + + +fn handle_field_hash_instruction( + avm_instrs: &mut Vec, + function: &String, + destinations: &Vec, + inputs: &Vec, +) { + + // handle field returns differently + let hash_offset_maybe = inputs[0]; + let (hash_offset, hash_size) = match hash_offset_maybe { + ValueOrArray::HeapArray(HeapArray { pointer, size}) => (pointer.0, size), + _ => panic!("Poseidon address inputs destination should be a single value"), + }; + + assert!(destinations.len() == 1); + let dest_offset_maybe = destinations[0]; + let dest_offset = match dest_offset_maybe { + ValueOrArray::MemoryAddress(dest_offset) => dest_offset.0, + _ => panic!("Poseidon address destination should be a single value"), + }; + + let opcode = match function.as_str() { + "poseidon" => AvmOpcode::POSEIDON, + _ => panic!("Transpiler doesn't know how to process ForeignCall function {:?}", function), + }; + + + avm_instrs.push(AvmInstruction { + opcode, + operands: vec![AvmOperand::U32 { + value: dest_offset as u32, + }, + AvmOperand::U32 { + value: hash_offset as u32, + }, + AvmOperand::U32 { + value: hash_size as u32, + }], + ..Default::default() + }); + } + +fn handle_getter_instruction( + avm_instrs: &mut Vec, + function: &String, + destinations: &Vec, + inputs: &Vec, ) { // For the foreign calls we want to handle, we do not want inputs, as they are getters assert!(inputs.is_empty()); @@ -260,15 +363,17 @@ fn handle_foreign_call( function ), }; +avm_instrs.push(AvmInstruction { + opcode, + indirect: Some(ALL_DIRECT), + operands: vec![AvmOperand::U32 { + value: dest_offset as u32, + }], + ..Default::default() +}) + + - avm_instrs.push(AvmInstruction { - opcode, - indirect: Some(ALL_DIRECT), - operands: vec![AvmOperand::U32 { - value: dest_offset as u32, - }], - ..Default::default() - }); } /// Handles Brillig's CONST opcode. @@ -356,17 +461,14 @@ fn emit_mov(indirect: Option, source: u32, dest: u32) -> AvmInstruction { fn handle_black_box_function(avm_instrs: &mut Vec, operation: &BlackBoxOp) { match operation { - BlackBoxOp::Keccak256 { message, output } => { - // For the meantime, we output keccak as 32 u8s - let hash_offset = message.pointer.0; - let hash_size = message.size.0; + BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { + let hash_offset = inputs.pointer.0; + let hash_size = inputs.size.0; - let out_offset = output.pointer.0; - let out_size = output.size; - assert!(out_size == 32); + let out_offset = output.0; avm_instrs.push(AvmInstruction { - opcode: AvmOpcode::KECCAK, + opcode: AvmOpcode::PEDERSEN, operands: vec![AvmOperand::U32 { value: hash_offset as u32, }, @@ -378,7 +480,7 @@ fn handle_black_box_function(avm_instrs: &mut Vec, operation: &B }], ..Default::default() }); - }, + } _ => panic!( "Transpiler doesn't know how to process BlackBoxOp {:?}", operation @@ -416,6 +518,7 @@ fn map_brillig_pcs_to_avm_pcs(initial_offset: usize, brillig: &Brillig) -> Vec AvmTypeTag { match bit_size { + 1 => AvmTypeTag::UINT8, // temp workaround 8 => AvmTypeTag::UINT8, 16 => AvmTypeTag::UINT16, 32 => AvmTypeTag::UINT32, diff --git a/noir-projects/aztec-nr/aztec/src/avm.nr b/noir-projects/aztec-nr/aztec/src/avm.nr index 3d9885db80d8..cfd9aa3e6f4b 100644 --- a/noir-projects/aztec-nr/aztec/src/avm.nr +++ b/noir-projects/aztec-nr/aztec/src/avm.nr @@ -1 +1,2 @@ mod context; +mod hash; diff --git a/noir-projects/aztec-nr/aztec/src/avm/hash.nr b/noir-projects/aztec-nr/aztec/src/avm/hash.nr new file mode 100644 index 000000000000..2c2ac6f2448b --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/avm/hash.nr @@ -0,0 +1,8 @@ +#[oracle(keccak256)] +pub fn keccak256(input: [Field; N]) -> [Field; 2] {} + +#[oracle(poseidon)] +pub fn poseidon(input: [Field; N]) -> Field {} + +#[oracle(sha256)] +pub fn sha256(input: [Field; N]) -> [Field; 2] {} diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index ef7e43d152f3..c5aa88bcff9c 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -1,11 +1,16 @@ -// - contract AvmTest { // Libs use dep::aztec::protocol_types::address::{AztecAddress, EthAddress}; // avm lib - use dep::aztec::avm::context::AvmContext; + use dep::aztec::avm::{ + context::AvmContext, + hash::{ + keccak256, + poseidon, + sha256 + } + }; #[aztec(private)] fn constructor() {} @@ -55,10 +60,19 @@ contract AvmTest { * Hashing functions ************************************************************************/ #[aztec(public-vm)] - fn oneFieldKeccak(data: [u8; 32]) -> pub Field { - // let data_as_bytes = data.to_be_bytes(32); - let output = dep::std::hash::keccak256(data, 32); - output[0] as Field + fn keccak(data: [Field; 3]) -> pub [Field; 2] { + keccak256(data) + } + + #[aztec(public-vm)] + fn poseidon_hash(data: [Field; 3]) -> pub Field { + let new_data = data; + poseidon(new_data) + } + + #[aztec(public-vm)] + fn sha256(data: [Field; 3]) -> pub [Field; 2] { + sha256(data) } /************************************************************************ diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 5133f1c5a4d2..d057186e2fa9 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -10,6 +10,7 @@ import { AvmSimulator } from './avm_simulator.js'; import { initContext, initExecutionEnvironment, initGlobalVariables } from './fixtures/index.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; import { encodeToBytecode } from './serialization/bytecode_serialization.js'; +import { poseidonHash } from '@aztec/foundation/crypto'; describe('AVM simulator', () => { it('Should execute bytecode that performs basic addition', async () => { @@ -82,6 +83,57 @@ describe('AVM simulator', () => { }); }); + describe("Hashes in noir contracts", () => { + it('Should execute contract function that performs Poseidon2 hash', async () => { + const calldata = [new Fr(1), new Fr(2), new Fr(3)]; + const hash = poseidonHash(calldata.map(f => f.toBuffer())); + console.log(hash); + + // Get contract function artifact + const poseidonArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_poseidon')!; + + // Decode bytecode into instructions + const bytecode = Buffer.from(poseidonArtifact.bytecode, 'base64'); + + const context = initContext({ env: initExecutionEnvironment({ calldata }) }); + jest + .spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode') + .mockReturnValue(Promise.resolve(bytecode)); + + const results = await new AvmSimulator(context).execute(); + + expect(results.reverted).toBe(false); + + const returnData = results.output; + console.log(returnData); + expect(returnData).toEqual([new Fr(hash)]); + }); + + // it('Should execute contract function that performs Keccak hash', async () => { + // const calldata = [new Fr(1), new Fr(2), new Fr(3)]; + // const hash = Fr.random(); + + // // Get contract function artifact + // const keccakArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_keccak')!; + + // // Decode bytecode into instructions + // const bytecode = Buffer.from(keccakArtifact.bytecode, 'base64'); + + // const context = initContext({ env: initExecutionEnvironment({ calldata }) }); + // jest + // .spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode') + // .mockReturnValue(Promise.resolve(bytecode)); + + // const results = await new AvmSimulator(context).execute(); + + // expect(results.reverted).toBe(false); + + // const returnData = results.output; + // expect(returnData).toEqual([hash.toField()]); + // }); + + }) + describe('Test env getters from noir contract', () => { const testEnvGetter = async (valueName: string, value: any, functionName: string, globalVar: boolean = false) => { const getterArtifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!; diff --git a/yarn-project/simulator/src/avm/avm_simulator.ts b/yarn-project/simulator/src/avm/avm_simulator.ts index 48b801e78f61..eae9a4a1fd93 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.ts @@ -18,6 +18,7 @@ export class AvmSimulator { */ public async execute(): Promise { const instructions = await this.fetchAndDecodeBytecode(); + console.log(instructions); return this.executeInstructions(instructions); } diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.ts b/yarn-project/simulator/src/avm/opcodes/hashing.ts index 5add968ac6e0..fb3da8e9b218 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.ts @@ -24,12 +24,17 @@ export class Poseidon2 extends Instruction { async execute(context: AvmContext): Promise { // We hash a set of field elements + console.log(context.machineState.memory); + // Memory pointer will be indirect + const hashDataPtr = context.machineState.memory.get(this.hashOffset); const hashData = context.machineState.memory - .getSlice(this.hashOffset, this.hashOffset + this.hashSize) + .getSlice(hashDataPtr., this.hashSize) .map(word => word.toBuffer()); + console.log("hashData: ", hashData); const hash = poseidonHash(hashData); context.machineState.memory.set(this.dstOffset, new Field(hash)); + console.log("hash: ", hash); context.machineState.incrementPc(); } @@ -55,7 +60,7 @@ export class Keccak extends Instruction { async execute(context: AvmContext): Promise { // We hash a set of field elements const hashData = context.machineState.memory - .getSlice(this.hashOffset, this.hashOffset + this.hashSize) + .getSlice(this.hashOffset, this.hashSize) .map(word => word.toBuffer()); const hash = keccak(Buffer.concat(hashData)); @@ -91,7 +96,7 @@ export class Sha256 extends Instruction { async execute(context: AvmContext): Promise { // We hash a set of field elements const hashData = context.machineState.memory - .getSlice(this.hashOffset, this.hashOffset + this.hashSize) + .getSlice(this.hashOffset, this.hashSize) .map(word => word.toBuffer()); const hash = sha256(Buffer.concat(hashData)); @@ -126,7 +131,7 @@ export class Pedersen extends Instruction { async execute(context: AvmContext): Promise { // We hash a set of field elements const hashData = context.machineState.memory - .getSlice(this.hashOffset, this.hashOffset + this.hashSize) + .getSlice(this.hashOffset, this.hashSize) .map(word => word.toBuffer()); // No domain sep for now diff --git a/yarn-project/simulator/src/avm/opcodes/memory.ts b/yarn-project/simulator/src/avm/opcodes/memory.ts index d8c9ad7c0aed..4f8f28e1f862 100644 --- a/yarn-project/simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/simulator/src/avm/opcodes/memory.ts @@ -186,6 +186,7 @@ export class CalldataCopy extends Instruction { const transformedData = context.environment.calldata .slice(this.cdOffset, this.cdOffset + this.copySize) .map(f => new Field(f)); + context.machineState.memory.setSlice(this.dstOffset, transformedData); context.machineState.incrementPc(); From 0794ec3b9a8c98e77816d45121f56d29a3367538 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 14 Feb 2024 22:34:03 +0000 Subject: [PATCH 5/9] fix: get e2e hashing working --- avm-transpiler/src/transpile.rs | 18 ++++-- .../contracts/avm_test_contract/src/main.nr | 24 +++++--- .../simulator/src/avm/avm_simulator.test.ts | 61 +++++++++++-------- .../simulator/src/avm/opcodes/hashing.test.ts | 30 ++++++--- .../simulator/src/avm/opcodes/hashing.ts | 53 +++++++++++----- 5 files changed, 118 insertions(+), 68 deletions(-) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 0581a7e926c1..06bd4ce8fc08 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -1,3 +1,5 @@ +use core::num; + use acvm::acir::brillig::Opcode as BrilligOpcode; use acvm::acir::circuit::brillig::Brillig; @@ -272,6 +274,7 @@ fn handle_2_field_hash_instruction( avm_instrs.push(AvmInstruction { opcode, + indirect: Some(3), // 11 - addressing mode, indirect for input and output operands: vec![AvmOperand::U32 { value: dest_offset as u32, }, @@ -316,6 +319,7 @@ fn handle_field_hash_instruction( avm_instrs.push(AvmInstruction { opcode, + indirect: Some(1), operands: vec![AvmOperand::U32 { value: dest_offset as u32, }, @@ -465,18 +469,19 @@ fn handle_black_box_function(avm_instrs: &mut Vec, operation: &B let hash_offset = inputs.pointer.0; let hash_size = inputs.size.0; - let out_offset = output.0; + let dest_offset = output.0; avm_instrs.push(AvmInstruction { opcode: AvmOpcode::PEDERSEN, + indirect: Some(1), operands: vec![AvmOperand::U32 { - value: hash_offset as u32, + value: dest_offset as u32, }, AvmOperand::U32 { - value: hash_size as u32, + value: hash_offset as u32, }, AvmOperand::U32 { - value: out_offset as u32, + value: hash_size as u32, }], ..Default::default() }); @@ -504,11 +509,12 @@ fn map_brillig_pcs_to_avm_pcs(initial_offset: usize, brillig: &Brillig) -> Vec 2, - BrilligOpcode::Store { .. } => 2, BrilligOpcode::Const { bit_size: 254, .. } => 2 , _ => 1, }; + if num_avm_instrs_for_this_brillig_instr > 1 { + println!("num_avm_instrs_for_this_brillig_instr: {:?}", &brillig.bytecode[i]); + } // next Brillig pc will map to an AVM pc offset by the // number of AVM instructions generated for this Brillig one pc_map[i + 1] = pc_map[i] + num_avm_instrs_for_this_brillig_instr; diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index c5aa88bcff9c..67e3086814cf 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -56,28 +56,32 @@ contract AvmTest { 200 as Field } - /************************************************************************ - * Hashing functions - ************************************************************************/ + // /************************************************************************ + // * Hashing functions + // ************************************************************************/ #[aztec(public-vm)] - fn keccak(data: [Field; 3]) -> pub [Field; 2] { + fn keccak_hash(data: [Field; 3]) -> pub [Field; 2] { keccak256(data) } #[aztec(public-vm)] fn poseidon_hash(data: [Field; 3]) -> pub Field { - let new_data = data; - poseidon(new_data) + poseidon(data) } #[aztec(public-vm)] - fn sha256(data: [Field; 3]) -> pub [Field; 2] { + fn sha256_hash(data: [Field; 3]) -> pub [Field; 2] { sha256(data) } - /************************************************************************ - * AvmContext functions - ************************************************************************/ + #[aztec(public-vm)] + fn pedersen_hash(data: [Field; 3]) -> pub Field { + dep::std::hash::pedersen_hash(data) + } + + // /************************************************************************ + // * AvmContext functions + // ************************************************************************/ #[aztec(public-vm)] fn getAddress() -> pub AztecAddress { context.address() diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index d057186e2fa9..8203f9afe114 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -10,7 +10,7 @@ import { AvmSimulator } from './avm_simulator.js'; import { initContext, initExecutionEnvironment, initGlobalVariables } from './fixtures/index.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; import { encodeToBytecode } from './serialization/bytecode_serialization.js'; -import { poseidonHash } from '@aztec/foundation/crypto'; +import { keccak, pedersenHash, poseidonHash, sha256 } from '@aztec/foundation/crypto'; describe('AVM simulator', () => { it('Should execute bytecode that performs basic addition', async () => { @@ -83,17 +83,19 @@ describe('AVM simulator', () => { }); }); - describe("Hashes in noir contracts", () => { - it('Should execute contract function that performs Poseidon2 hash', async () => { + describe.each([ + ["avm_sha256_hash", sha256], + ["avm_keccak_hash", keccak] + ])("Hashes with 2 fields returned in noir contracts", (name: string, hashFunction: (data: Buffer)=> Buffer) => { + it(`Should execute contract function that performs ${name} hash`, async () => { const calldata = [new Fr(1), new Fr(2), new Fr(3)]; - const hash = poseidonHash(calldata.map(f => f.toBuffer())); - console.log(hash); + const hash = hashFunction(Buffer.concat(calldata.map(f => f.toBuffer()))); // Get contract function artifact - const poseidonArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_poseidon')!; + const artifact = AvmTestContractArtifact.functions.find(f => f.name === name)!; // Decode bytecode into instructions - const bytecode = Buffer.from(poseidonArtifact.bytecode, 'base64'); + const bytecode = Buffer.from(artifact.bytecode, 'base64'); const context = initContext({ env: initExecutionEnvironment({ calldata }) }); jest @@ -105,34 +107,41 @@ describe('AVM simulator', () => { expect(results.reverted).toBe(false); const returnData = results.output; - console.log(returnData); - expect(returnData).toEqual([new Fr(hash)]); + const reconstructedHash = Buffer.concat([returnData[0].toBuffer().subarray(16, 32), returnData[1].toBuffer().subarray(16, 32)]); + expect(reconstructedHash ).toEqual(hash); }); - // it('Should execute contract function that performs Keccak hash', async () => { - // const calldata = [new Fr(1), new Fr(2), new Fr(3)]; - // const hash = Fr.random(); + }) + + describe.each([ + ["avm_poseidon_hash", poseidonHash], + ["avm_pedersen_hash", pedersenHash] + ])("Hashes with field returned in noir contracts", (name: string, hashFunction: (data: Buffer[])=> Buffer) => { + + it(`Should execute contract function that performs ${name} hash`, async () => { + const calldata = [new Fr(1), new Fr(2), new Fr(3)]; + const hash = hashFunction(calldata.map(f => f.toBuffer())); - // // Get contract function artifact - // const keccakArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_keccak')!; + // Get contract function artifact + const artifact = AvmTestContractArtifact.functions.find(f => f.name === name)!; - // // Decode bytecode into instructions - // const bytecode = Buffer.from(keccakArtifact.bytecode, 'base64'); + // Decode bytecode into instructions + const bytecode = Buffer.from(artifact.bytecode, 'base64'); - // const context = initContext({ env: initExecutionEnvironment({ calldata }) }); - // jest - // .spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode') - // .mockReturnValue(Promise.resolve(bytecode)); + const context = initContext({ env: initExecutionEnvironment({ calldata }) }); + jest + .spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode') + .mockReturnValue(Promise.resolve(bytecode)); - // const results = await new AvmSimulator(context).execute(); + const results = await new AvmSimulator(context).execute(); - // expect(results.reverted).toBe(false); + expect(results.reverted).toBe(false); - // const returnData = results.output; - // expect(returnData).toEqual([hash.toField()]); - // }); + const returnData = results.output; + expect(returnData).toEqual([new Fr(hash)]); + }); + }); - }) describe('Test env getters from noir contract', () => { const testEnvGetter = async (valueName: string, value: any, functionName: string, globalVar: boolean = false) => { diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.test.ts b/yarn-project/simulator/src/avm/opcodes/hashing.test.ts index 48e7206385a0..768878307f4e 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.test.ts @@ -17,17 +17,19 @@ describe('Hashing Opcodes', () => { it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Poseidon2.opcode, // opcode + 1, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ...Buffer.from('23456789', 'hex'), // hashOffset ...Buffer.from('3456789a', 'hex'), // hashSize ]); - const inst = new Poseidon2(/*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); + const inst = new Poseidon2(/*indirect=*/ 1, /*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); expect(Poseidon2.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); - it('Should hash correctly', async () => { + it('Should hash correctly - direct', async () => { + const indirect = 0; const args = [new Field(1n), new Field(2n), new Field(3n)]; const hashOffset = 0; context.machineState.memory.setSlice(hashOffset, args); @@ -35,29 +37,32 @@ describe('Hashing Opcodes', () => { const dstOffset = 3; const expectedHash = poseidonHash(args.map(field => field.toBuffer())); - await new Poseidon2(dstOffset, hashOffset, args.length).execute(context); + await new Poseidon2(indirect, dstOffset, hashOffset, args.length).execute(context); const result = context.machineState.memory.get(dstOffset); expect(result).toEqual(new Field(toBigIntBE(expectedHash))); }); + // TODO: indirect }); describe('Keccak', () => { it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Keccak.opcode, // opcode + 1, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ...Buffer.from('23456789', 'hex'), // hashOffset ...Buffer.from('3456789a', 'hex'), // hashSize ]); - const inst = new Keccak(/*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); + const inst = new Keccak(/*indirect=*/1, /*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); expect(Keccak.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); - it('Should hash correctly', async () => { + it('Should hash correctly - direct', async () => { const args = [new Field(1n), new Field(2n), new Field(3n)]; + const indirect = 0; const hashOffset = 0; context.machineState.memory.setSlice(hashOffset, args); @@ -65,24 +70,26 @@ describe('Hashing Opcodes', () => { const inputBuffer = Buffer.concat(args.map(field => field.toBuffer())); const expectedHash = keccak(inputBuffer); - await new Keccak(dstOffset, hashOffset, args.length).execute(context); + await new Keccak(indirect, dstOffset, hashOffset, args.length).execute(context); const result = context.machineState.memory.getSliceAs(dstOffset, 2); const combined = Buffer.concat([result[0].toBuffer().subarray(16, 32), result[1].toBuffer().subarray(16, 32)]); expect(combined).toEqual(expectedHash); }); + // TODO: indirect }); describe('Sha256', () => { it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Sha256.opcode, // opcode + 1, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ...Buffer.from('23456789', 'hex'), // hashOffset ...Buffer.from('3456789a', 'hex'), // hashSize ]); - const inst = new Sha256(/*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); + const inst = new Sha256(/*indirect=*/1, /*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); expect(Sha256.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); @@ -91,13 +98,14 @@ describe('Hashing Opcodes', () => { it('Should hash correctly', async () => { const args = [new Field(1n), new Field(2n), new Field(3n)]; const hashOffset = 0; + const indirect = 0; context.machineState.memory.setSlice(hashOffset, args); const dstOffset = 3; const inputBuffer = Buffer.concat(args.map(field => field.toBuffer())); const expectedHash = sha256(inputBuffer); - await new Sha256(dstOffset, hashOffset, args.length).execute(context); + await new Sha256(indirect, dstOffset, hashOffset, args.length).execute(context); const result = context.machineState.memory.getSliceAs(dstOffset, 2); const combined = Buffer.concat([result[0].toBuffer().subarray(16, 32), result[1].toBuffer().subarray(16, 32)]); @@ -110,11 +118,12 @@ describe('Hashing Opcodes', () => { it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Pedersen.opcode, // opcode + 1, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ...Buffer.from('23456789', 'hex'), // hashOffset ...Buffer.from('3456789a', 'hex'), // hashSize ]); - const inst = new Pedersen(/*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); + const inst = new Pedersen(/*indirect=*/ 1,/*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); expect(Sha256.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); @@ -123,13 +132,14 @@ describe('Hashing Opcodes', () => { it('Should hash correctly', async () => { const args = [new Field(1n), new Field(2n), new Field(3n)]; const hashOffset = 0; + const indirect = 0; context.machineState.memory.setSlice(hashOffset, args); const dstOffset = 3; const inputBuffer = args.map(field => field.toBuffer()); const expectedHash = pedersenHash(inputBuffer); - await new Pedersen(dstOffset, hashOffset, args.length).execute(context); + await new Pedersen(indirect, dstOffset, hashOffset, args.length).execute(context); const result = context.machineState.memory.get(dstOffset); expect(result).toEqual(new Field(toBigIntBE(expectedHash))); diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.ts b/yarn-project/simulator/src/avm/opcodes/hashing.ts index fb3da8e9b218..fe0ca10af323 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.ts @@ -5,6 +5,7 @@ import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; +import { Addressing } from './addressing_mode.js'; export class Poseidon2 extends Instruction { static type: string = 'POSEIDON2'; @@ -12,29 +13,31 @@ export class Poseidon2 extends Instruction { // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32, OperandType.UINT32, ]; - constructor(private dstOffset: number, private hashOffset: number, private hashSize: number) { + constructor(private indirect: number, private dstOffset: number, private hashOffset: number, private hashSize: number) { super(); } async execute(context: AvmContext): Promise { // We hash a set of field elements - console.log(context.machineState.memory); + const [hashOffset ] = Addressing.fromWire(this.indirect).resolve( + [this.hashOffset], + context.machineState.memory, + ); + // Memory pointer will be indirect - const hashDataPtr = context.machineState.memory.get(this.hashOffset); const hashData = context.machineState.memory - .getSlice(hashDataPtr., this.hashSize) + .getSlice(hashOffset, this.hashSize) .map(word => word.toBuffer()); - console.log("hashData: ", hashData); const hash = poseidonHash(hashData); context.machineState.memory.set(this.dstOffset, new Field(hash)); - console.log("hash: ", hash); context.machineState.incrementPc(); } @@ -46,21 +49,27 @@ export class Keccak extends Instruction { // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32, OperandType.UINT32, ]; - constructor(private dstOffset: number, private hashOffset: number, private hashSize: number) { + constructor(private indirect: number, private dstOffset: number, private hashOffset: number, private hashSize: number) { super(); } // Note hash output is 32 bytes, so takes up two fields async execute(context: AvmContext): Promise { // We hash a set of field elements + const [hashOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( + [this.hashOffset, this.dstOffset], + context.machineState.memory, + ); + const hashData = context.machineState.memory - .getSlice(this.hashOffset, this.hashSize) + .getSlice(hashOffset, this.hashSize) .map(word => word.toBuffer()); const hash = keccak(Buffer.concat(hashData)); @@ -69,8 +78,8 @@ export class Keccak extends Instruction { const high = new Field(toBigIntBE(hash.subarray(0, 16))); const low = new Field(toBigIntBE(hash.subarray(16, 32))); - context.machineState.memory.set(this.dstOffset, high); - context.machineState.memory.set(this.dstOffset + 1, low); + context.machineState.memory.set(dstOffset, high); + context.machineState.memory.set(dstOffset + 1, low); context.machineState.incrementPc(); } @@ -82,21 +91,27 @@ export class Sha256 extends Instruction { // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32, OperandType.UINT32, ]; - constructor(private dstOffset: number, private hashOffset: number, private hashSize: number) { + constructor(private indirect: number, private dstOffset: number, private hashOffset: number, private hashSize: number) { super(); } // Note hash output is 32 bytes, so takes up two fields async execute(context: AvmContext): Promise { + const [hashOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( + [this.hashOffset, this.dstOffset], + context.machineState.memory, + ); + // We hash a set of field elements const hashData = context.machineState.memory - .getSlice(this.hashOffset, this.hashSize) + .getSlice(hashOffset, this.hashSize) .map(word => word.toBuffer()); const hash = sha256(Buffer.concat(hashData)); @@ -105,8 +120,8 @@ export class Sha256 extends Instruction { const high = new Field(toBigIntBE(hash.subarray(0, 16))); const low = new Field(toBigIntBE(hash.subarray(16, 32))); - context.machineState.memory.set(this.dstOffset, high); - context.machineState.memory.set(this.dstOffset + 1, low); + context.machineState.memory.set(dstOffset, high); + context.machineState.memory.set(dstOffset + 1, low); context.machineState.incrementPc(); } @@ -118,20 +133,26 @@ export class Pedersen extends Instruction { // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32, OperandType.UINT32, ]; - constructor(private dstOffset: number, private hashOffset: number, private hashSize: number) { + constructor(private indirect: number, private dstOffset: number, private hashOffset: number, private hashSize: number) { super(); } async execute(context: AvmContext): Promise { + const [hashOffset] = Addressing.fromWire(this.indirect).resolve( + [this.hashOffset], + context.machineState.memory, + ); + // We hash a set of field elements const hashData = context.machineState.memory - .getSlice(this.hashOffset, this.hashSize) + .getSlice(hashOffset, this.hashSize) .map(word => word.toBuffer()); // No domain sep for now From 07408d4367f92fb7eb424921e24f1e8347a7b116 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 14 Feb 2024 22:44:05 +0000 Subject: [PATCH 6/9] sweep --- avm-transpiler/src/opcodes.rs | 6 +- avm-transpiler/src/transpile.rs | 157 ++++++++++-------- .../simulator/src/avm/avm_simulator.test.ts | 26 +-- .../simulator/src/avm/avm_simulator.ts | 1 - .../simulator/src/avm/opcodes/hashing.test.ts | 118 ++++++++++++- .../simulator/src/avm/opcodes/hashing.ts | 56 ++++--- 6 files changed, 242 insertions(+), 122 deletions(-) diff --git a/avm-transpiler/src/opcodes.rs b/avm-transpiler/src/opcodes.rs index ef91f2b368da..84414239aa9c 100644 --- a/avm-transpiler/src/opcodes.rs +++ b/avm-transpiler/src/opcodes.rs @@ -150,10 +150,10 @@ impl AvmOpcode { // World State AvmOpcode::SLOAD => "SLOAD", // Public Storage AvmOpcode::SSTORE => "SSTORE", // Public Storage - AvmOpcode::NOTEHASHEXISTS => "NOTEHASHEXISTS", // Notes & Nullifiers - AvmOpcode::EMITNOTEHASH => "EMITNOTEHASH", // Notes & Nullifiers + AvmOpcode::NOTEHASHEXISTS => "NOTEHASHEXISTS", // Notes & Nullifiers + AvmOpcode::EMITNOTEHASH => "EMITNOTEHASH", // Notes & Nullifiers AvmOpcode::NULLIFIEREXISTS => "NULLIFIEREXISTS", // Notes & Nullifiers - AvmOpcode::EMITNULLIFIER => "EMITNULLIFIER", // Notes & Nullifiers + AvmOpcode::EMITNULLIFIER => "EMITNULLIFIER", // Notes & Nullifiers AvmOpcode::READL1TOL2MSG => "READL1TOL2MSG", // Messages AvmOpcode::HEADERMEMBER => "HEADERMEMBER", // Archive tree & Headers diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 06bd4ce8fc08..53e11a294065 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -1,9 +1,9 @@ -use core::num; - use acvm::acir::brillig::Opcode as BrilligOpcode; use acvm::acir::circuit::brillig::Brillig; -use acvm::brillig_vm::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, MemoryAddress, Value, ValueOrArray}; +use acvm::brillig_vm::brillig::{ + BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, MemoryAddress, Value, ValueOrArray, +}; use crate::instructions::{ AvmInstruction, AvmOperand, AvmTypeTag, ALL_DIRECT, FIRST_OPERAND_INDIRECT, @@ -235,72 +235,75 @@ fn handle_foreign_call( inputs: &Vec, ) { match function.as_str() { - "keccak256" | "sha256" => handle_2_field_hash_instruction(avm_instrs, function, destinations, inputs), + "keccak256" | "sha256" => { + handle_2_field_hash_instruction(avm_instrs, function, destinations, inputs) + } "poseidon" => handle_field_hash_instruction(avm_instrs, function, destinations, inputs), - _ => handle_getter_instruction(avm_instrs, function, destinations, inputs) + _ => handle_getter_instruction(avm_instrs, function, destinations, inputs), } } fn handle_2_field_hash_instruction( avm_instrs: &mut Vec, function: &String, - destinations: &Vec, - inputs: &Vec, + destinations: &[ValueOrArray], + inputs: &[ValueOrArray], ) { // handle field returns differently let hash_offset_maybe = inputs[0]; println!("hash_offset_maybe: {:?}", hash_offset_maybe); let (hash_offset, hash_size) = match hash_offset_maybe { - ValueOrArray::HeapArray(HeapArray { pointer, size}) => (pointer.0, size), + ValueOrArray::HeapArray(HeapArray { pointer, size }) => (pointer.0, size), _ => panic!("Keccak | Sha256 address inputs destination should be a single value"), }; assert!(destinations.len() == 1); let dest_offset_maybe = destinations[0]; let dest_offset = match dest_offset_maybe { - ValueOrArray::HeapArray(HeapArray { pointer, size}) => { + ValueOrArray::HeapArray(HeapArray { pointer, size }) => { assert!(size == 2); pointer.0 - }, + } _ => panic!("Keccak | Poseidon address destination should be a single value"), }; let opcode = match function.as_str() { "keccak256" => AvmOpcode::KECCAK, "sha256" => AvmOpcode::SHA256, - _ => panic!("Transpiler doesn't know how to process ForeignCall function {:?}", function), + _ => panic!( + "Transpiler doesn't know how to process ForeignCall function {:?}", + function + ), }; - - avm_instrs.push(AvmInstruction { - opcode, - indirect: Some(3), // 11 - addressing mode, indirect for input and output - operands: vec![AvmOperand::U32 { - value: dest_offset as u32, - }, - AvmOperand::U32 { - value: hash_offset as u32, - }, - AvmOperand::U32 { - value: hash_size as u32, - }], - ..Default::default() - }); - + avm_instrs.push(AvmInstruction { + opcode, + indirect: Some(3), // 11 - addressing mode, indirect for input and output + operands: vec![ + AvmOperand::U32 { + value: dest_offset as u32, + }, + AvmOperand::U32 { + value: hash_offset as u32, + }, + AvmOperand::U32 { + value: hash_size as u32, + }, + ], + ..Default::default() + }); } - fn handle_field_hash_instruction( avm_instrs: &mut Vec, function: &String, - destinations: &Vec, - inputs: &Vec, + destinations: &[ValueOrArray], + inputs: &[ValueOrArray], ) { - // handle field returns differently let hash_offset_maybe = inputs[0]; let (hash_offset, hash_size) = match hash_offset_maybe { - ValueOrArray::HeapArray(HeapArray { pointer, size}) => (pointer.0, size), + ValueOrArray::HeapArray(HeapArray { pointer, size }) => (pointer.0, size), _ => panic!("Poseidon address inputs destination should be a single value"), }; @@ -313,25 +316,29 @@ fn handle_field_hash_instruction( let opcode = match function.as_str() { "poseidon" => AvmOpcode::POSEIDON, - _ => panic!("Transpiler doesn't know how to process ForeignCall function {:?}", function), + _ => panic!( + "Transpiler doesn't know how to process ForeignCall function {:?}", + function + ), }; - - avm_instrs.push(AvmInstruction { - opcode, - indirect: Some(1), - operands: vec![AvmOperand::U32 { - value: dest_offset as u32, - }, - AvmOperand::U32 { - value: hash_offset as u32, - }, - AvmOperand::U32 { - value: hash_size as u32, - }], - ..Default::default() - }); - } + avm_instrs.push(AvmInstruction { + opcode, + indirect: Some(1), + operands: vec![ + AvmOperand::U32 { + value: dest_offset as u32, + }, + AvmOperand::U32 { + value: hash_offset as u32, + }, + AvmOperand::U32 { + value: hash_size as u32, + }, + ], + ..Default::default() + }); +} fn handle_getter_instruction( avm_instrs: &mut Vec, @@ -367,17 +374,14 @@ fn handle_getter_instruction( function ), }; -avm_instrs.push(AvmInstruction { - opcode, - indirect: Some(ALL_DIRECT), - operands: vec![AvmOperand::U32 { - value: dest_offset as u32, - }], - ..Default::default() -}) - - - + avm_instrs.push(AvmInstruction { + opcode, + indirect: Some(ALL_DIRECT), + operands: vec![AvmOperand::U32 { + value: dest_offset as u32, + }], + ..Default::default() + }) } /// Handles Brillig's CONST opcode. @@ -465,7 +469,11 @@ fn emit_mov(indirect: Option, source: u32, dest: u32) -> AvmInstruction { fn handle_black_box_function(avm_instrs: &mut Vec, operation: &BlackBoxOp) { match operation { - BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { + BlackBoxOp::PedersenHash { + inputs, + domain_separator: _, + output, + } => { let hash_offset = inputs.pointer.0; let hash_size = inputs.size.0; @@ -474,15 +482,17 @@ fn handle_black_box_function(avm_instrs: &mut Vec, operation: &B avm_instrs.push(AvmInstruction { opcode: AvmOpcode::PEDERSEN, indirect: Some(1), - operands: vec![AvmOperand::U32 { - value: dest_offset as u32, - }, - AvmOperand::U32 { - value: hash_offset as u32, - }, - AvmOperand::U32 { - value: hash_size as u32, - }], + operands: vec![ + AvmOperand::U32 { + value: dest_offset as u32, + }, + AvmOperand::U32 { + value: hash_offset as u32, + }, + AvmOperand::U32 { + value: hash_size as u32, + }, + ], ..Default::default() }); } @@ -509,11 +519,14 @@ fn map_brillig_pcs_to_avm_pcs(initial_offset: usize, brillig: &Brillig) -> Vec 2 , + BrilligOpcode::Const { bit_size: 254, .. } => 2, _ => 1, }; if num_avm_instrs_for_this_brillig_instr > 1 { - println!("num_avm_instrs_for_this_brillig_instr: {:?}", &brillig.bytecode[i]); + println!( + "num_avm_instrs_for_this_brillig_instr: {:?}", + &brillig.bytecode[i] + ); } // next Brillig pc will map to an AVM pc offset by the // number of AVM instructions generated for this Brillig one diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 8203f9afe114..8a56550155ae 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -1,4 +1,5 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { keccak, pedersenHash, poseidonHash, sha256 } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { AvmTestContractArtifact } from '@aztec/noir-contracts.js'; @@ -10,7 +11,6 @@ import { AvmSimulator } from './avm_simulator.js'; import { initContext, initExecutionEnvironment, initGlobalVariables } from './fixtures/index.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; import { encodeToBytecode } from './serialization/bytecode_serialization.js'; -import { keccak, pedersenHash, poseidonHash, sha256 } from '@aztec/foundation/crypto'; describe('AVM simulator', () => { it('Should execute bytecode that performs basic addition', async () => { @@ -84,9 +84,9 @@ describe('AVM simulator', () => { }); describe.each([ - ["avm_sha256_hash", sha256], - ["avm_keccak_hash", keccak] - ])("Hashes with 2 fields returned in noir contracts", (name: string, hashFunction: (data: Buffer)=> Buffer) => { + ['avm_sha256_hash', sha256], + ['avm_keccak_hash', keccak], + ])('Hashes with 2 fields returned in noir contracts', (name: string, hashFunction: (data: Buffer) => Buffer) => { it(`Should execute contract function that performs ${name} hash`, async () => { const calldata = [new Fr(1), new Fr(2), new Fr(3)]; const hash = hashFunction(Buffer.concat(calldata.map(f => f.toBuffer()))); @@ -107,17 +107,18 @@ describe('AVM simulator', () => { expect(results.reverted).toBe(false); const returnData = results.output; - const reconstructedHash = Buffer.concat([returnData[0].toBuffer().subarray(16, 32), returnData[1].toBuffer().subarray(16, 32)]); - expect(reconstructedHash ).toEqual(hash); + const reconstructedHash = Buffer.concat([ + returnData[0].toBuffer().subarray(16, 32), + returnData[1].toBuffer().subarray(16, 32), + ]); + expect(reconstructedHash).toEqual(hash); }); - - }) + }); describe.each([ - ["avm_poseidon_hash", poseidonHash], - ["avm_pedersen_hash", pedersenHash] - ])("Hashes with field returned in noir contracts", (name: string, hashFunction: (data: Buffer[])=> Buffer) => { - + ['avm_poseidon_hash', poseidonHash], + ['avm_pedersen_hash', pedersenHash], + ])('Hashes with field returned in noir contracts', (name: string, hashFunction: (data: Buffer[]) => Buffer) => { it(`Should execute contract function that performs ${name} hash`, async () => { const calldata = [new Fr(1), new Fr(2), new Fr(3)]; const hash = hashFunction(calldata.map(f => f.toBuffer())); @@ -142,7 +143,6 @@ describe('AVM simulator', () => { }); }); - describe('Test env getters from noir contract', () => { const testEnvGetter = async (valueName: string, value: any, functionName: string, globalVar: boolean = false) => { const getterArtifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!; diff --git a/yarn-project/simulator/src/avm/avm_simulator.ts b/yarn-project/simulator/src/avm/avm_simulator.ts index eae9a4a1fd93..48b801e78f61 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.ts @@ -18,7 +18,6 @@ export class AvmSimulator { */ public async execute(): Promise { const instructions = await this.fetchAndDecodeBytecode(); - console.log(instructions); return this.executeInstructions(instructions); } diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.test.ts b/yarn-project/simulator/src/avm/opcodes/hashing.test.ts index 768878307f4e..0c7b363df048 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.test.ts @@ -2,7 +2,7 @@ import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; import { keccak, pedersenHash, poseidonHash, sha256 } from '@aztec/foundation/crypto'; import { AvmContext } from '../avm_context.js'; -import { Field } from '../avm_memory_types.js'; +import { Field, Uint32 } from '../avm_memory_types.js'; import { initContext } from '../fixtures/index.js'; import { Keccak, Pedersen, Poseidon2, Sha256 } from './hashing.js'; @@ -22,7 +22,12 @@ describe('Hashing Opcodes', () => { ...Buffer.from('23456789', 'hex'), // hashOffset ...Buffer.from('3456789a', 'hex'), // hashSize ]); - const inst = new Poseidon2(/*indirect=*/ 1, /*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); + const inst = new Poseidon2( + /*indirect=*/ 1, + /*dstOffset=*/ 0x12345678, + /*hashOffset=*/ 0x23456789, + /*hashSize=*/ 0x3456789a, + ); expect(Poseidon2.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); @@ -42,7 +47,24 @@ describe('Hashing Opcodes', () => { const result = context.machineState.memory.get(dstOffset); expect(result).toEqual(new Field(toBigIntBE(expectedHash))); }); - // TODO: indirect + + it('Should hash correctly - indirect', async () => { + const args = [new Field(1n), new Field(2n), new Field(3n)]; + const indirect = 1; + const hashOffset = 0; + const realLocation = 4; + + context.machineState.memory.set(hashOffset, new Uint32(realLocation)); + context.machineState.memory.setSlice(realLocation, args); + + const dstOffset = 3; + + const expectedHash = poseidonHash(args.map(field => field.toBuffer())); + await new Poseidon2(indirect, dstOffset, hashOffset, args.length).execute(context); + + const result = context.machineState.memory.get(dstOffset); + expect(result).toEqual(new Field(toBigIntBE(expectedHash))); + }); }); describe('Keccak', () => { @@ -54,7 +76,12 @@ describe('Hashing Opcodes', () => { ...Buffer.from('23456789', 'hex'), // hashOffset ...Buffer.from('3456789a', 'hex'), // hashSize ]); - const inst = new Keccak(/*indirect=*/1, /*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); + const inst = new Keccak( + /*indirect=*/ 1, + /*dstOffset=*/ 0x12345678, + /*hashOffset=*/ 0x23456789, + /*hashSize=*/ 0x3456789a, + ); expect(Keccak.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); @@ -77,6 +104,29 @@ describe('Hashing Opcodes', () => { expect(combined).toEqual(expectedHash); }); + + it('Should hash correctly - indirect', async () => { + const args = [new Field(1n), new Field(2n), new Field(3n)]; + const indirect = 3; // dest and return are indirect + const hashOffset = 0; + const argsLocation = 4; + + const dstOffset = 2; + const readLocation = 6; + + context.machineState.memory.set(hashOffset, new Uint32(argsLocation)); + context.machineState.memory.set(dstOffset, new Uint32(readLocation)); + context.machineState.memory.setSlice(argsLocation, args); + + const inputBuffer = Buffer.concat(args.map(field => field.toBuffer())); + const expectedHash = keccak(inputBuffer); + await new Keccak(indirect, dstOffset, hashOffset, args.length).execute(context); + + const result = context.machineState.memory.getSliceAs(readLocation, 2); + const combined = Buffer.concat([result[0].toBuffer().subarray(16, 32), result[1].toBuffer().subarray(16, 32)]); + + expect(combined).toEqual(expectedHash); + }); // TODO: indirect }); @@ -89,13 +139,18 @@ describe('Hashing Opcodes', () => { ...Buffer.from('23456789', 'hex'), // hashOffset ...Buffer.from('3456789a', 'hex'), // hashSize ]); - const inst = new Sha256(/*indirect=*/1, /*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); + const inst = new Sha256( + /*indirect=*/ 1, + /*dstOffset=*/ 0x12345678, + /*hashOffset=*/ 0x23456789, + /*hashSize=*/ 0x3456789a, + ); expect(Sha256.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); - it('Should hash correctly', async () => { + it('Should hash correctly - direct', async () => { const args = [new Field(1n), new Field(2n), new Field(3n)]; const hashOffset = 0; const indirect = 0; @@ -112,6 +167,29 @@ describe('Hashing Opcodes', () => { expect(combined).toEqual(expectedHash); }); + + it('Should hash correctly - indirect', async () => { + const args = [new Field(1n), new Field(2n), new Field(3n)]; + const indirect = 3; // dest and return are indirect + const hashOffset = 0; + const argsLocation = 4; + + const dstOffset = 2; + const readLocation = 6; + + context.machineState.memory.set(hashOffset, new Uint32(argsLocation)); + context.machineState.memory.set(dstOffset, new Uint32(readLocation)); + context.machineState.memory.setSlice(argsLocation, args); + + const inputBuffer = Buffer.concat(args.map(field => field.toBuffer())); + const expectedHash = sha256(inputBuffer); + await new Sha256(indirect, dstOffset, hashOffset, args.length).execute(context); + + const result = context.machineState.memory.getSliceAs(readLocation, 2); + const combined = Buffer.concat([result[0].toBuffer().subarray(16, 32), result[1].toBuffer().subarray(16, 32)]); + + expect(combined).toEqual(expectedHash); + }); }); describe('Pedersen', () => { @@ -123,13 +201,18 @@ describe('Hashing Opcodes', () => { ...Buffer.from('23456789', 'hex'), // hashOffset ...Buffer.from('3456789a', 'hex'), // hashSize ]); - const inst = new Pedersen(/*indirect=*/ 1,/*dstOffset=*/ 0x12345678, /*hashOffset=*/ 0x23456789, /*hashSize=*/ 0x3456789a); + const inst = new Pedersen( + /*indirect=*/ 1, + /*dstOffset=*/ 0x12345678, + /*hashOffset=*/ 0x23456789, + /*hashSize=*/ 0x3456789a, + ); expect(Sha256.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); - it('Should hash correctly', async () => { + it('Should hash correctly - direct', async () => { const args = [new Field(1n), new Field(2n), new Field(3n)]; const hashOffset = 0; const indirect = 0; @@ -144,5 +227,24 @@ describe('Hashing Opcodes', () => { const result = context.machineState.memory.get(dstOffset); expect(result).toEqual(new Field(toBigIntBE(expectedHash))); }); + + it('Should hash correctly - indirect', async () => { + const args = [new Field(1n), new Field(2n), new Field(3n)]; + const indirect = 1; + const hashOffset = 0; + const realLocation = 4; + + context.machineState.memory.set(hashOffset, new Uint32(realLocation)); + context.machineState.memory.setSlice(realLocation, args); + + const dstOffset = 3; + + const inputBuffer = args.map(field => field.toBuffer()); + const expectedHash = pedersenHash(inputBuffer); + await new Pedersen(indirect, dstOffset, hashOffset, args.length).execute(context); + + const result = context.machineState.memory.get(dstOffset); + expect(result).toEqual(new Field(toBigIntBE(expectedHash))); + }); }); }); diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.ts b/yarn-project/simulator/src/avm/opcodes/hashing.ts index fe0ca10af323..99e64b0a4439 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.ts @@ -4,8 +4,8 @@ import { keccak, pedersenHash, poseidonHash, sha256 } from '@aztec/foundation/cr import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; -import { Instruction } from './instruction.js'; import { Addressing } from './addressing_mode.js'; +import { Instruction } from './instruction.js'; export class Poseidon2 extends Instruction { static type: string = 'POSEIDON2'; @@ -20,21 +20,21 @@ export class Poseidon2 extends Instruction { OperandType.UINT32, ]; - constructor(private indirect: number, private dstOffset: number, private hashOffset: number, private hashSize: number) { + constructor( + private indirect: number, + private dstOffset: number, + private hashOffset: number, + private hashSize: number, + ) { super(); } async execute(context: AvmContext): Promise { // We hash a set of field elements - const [hashOffset ] = Addressing.fromWire(this.indirect).resolve( - [this.hashOffset], - context.machineState.memory, - ); + const [hashOffset] = Addressing.fromWire(this.indirect).resolve([this.hashOffset], context.machineState.memory); // Memory pointer will be indirect - const hashData = context.machineState.memory - .getSlice(hashOffset, this.hashSize) - .map(word => word.toBuffer()); + const hashData = context.machineState.memory.getSlice(hashOffset, this.hashSize).map(word => word.toBuffer()); const hash = poseidonHash(hashData); context.machineState.memory.set(this.dstOffset, new Field(hash)); @@ -56,7 +56,12 @@ export class Keccak extends Instruction { OperandType.UINT32, ]; - constructor(private indirect: number, private dstOffset: number, private hashOffset: number, private hashSize: number) { + constructor( + private indirect: number, + private dstOffset: number, + private hashOffset: number, + private hashSize: number, + ) { super(); } @@ -68,9 +73,7 @@ export class Keccak extends Instruction { context.machineState.memory, ); - const hashData = context.machineState.memory - .getSlice(hashOffset, this.hashSize) - .map(word => word.toBuffer()); + const hashData = context.machineState.memory.getSlice(hashOffset, this.hashSize).map(word => word.toBuffer()); const hash = keccak(Buffer.concat(hashData)); @@ -98,7 +101,12 @@ export class Sha256 extends Instruction { OperandType.UINT32, ]; - constructor(private indirect: number, private dstOffset: number, private hashOffset: number, private hashSize: number) { + constructor( + private indirect: number, + private dstOffset: number, + private hashOffset: number, + private hashSize: number, + ) { super(); } @@ -110,9 +118,7 @@ export class Sha256 extends Instruction { ); // We hash a set of field elements - const hashData = context.machineState.memory - .getSlice(hashOffset, this.hashSize) - .map(word => word.toBuffer()); + const hashData = context.machineState.memory.getSlice(hashOffset, this.hashSize).map(word => word.toBuffer()); const hash = sha256(Buffer.concat(hashData)); @@ -140,20 +146,20 @@ export class Pedersen extends Instruction { OperandType.UINT32, ]; - constructor(private indirect: number, private dstOffset: number, private hashOffset: number, private hashSize: number) { + constructor( + private indirect: number, + private dstOffset: number, + private hashOffset: number, + private hashSize: number, + ) { super(); } async execute(context: AvmContext): Promise { - const [hashOffset] = Addressing.fromWire(this.indirect).resolve( - [this.hashOffset], - context.machineState.memory, - ); + const [hashOffset] = Addressing.fromWire(this.indirect).resolve([this.hashOffset], context.machineState.memory); // We hash a set of field elements - const hashData = context.machineState.memory - .getSlice(hashOffset, this.hashSize) - .map(word => word.toBuffer()); + const hashData = context.machineState.memory.getSlice(hashOffset, this.hashSize).map(word => word.toBuffer()); // No domain sep for now const hash = pedersenHash(hashData); From b3fce3c34bf29f1acec711024b3fd7023f1fb47d Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 14 Feb 2024 22:48:41 +0000 Subject: [PATCH 7/9] sweep 2 --- avm-transpiler/src/transpile.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 53e11a294065..4beb63a6e7b3 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -522,12 +522,6 @@ fn map_brillig_pcs_to_avm_pcs(initial_offset: usize, brillig: &Brillig) -> Vec 2, _ => 1, }; - if num_avm_instrs_for_this_brillig_instr > 1 { - println!( - "num_avm_instrs_for_this_brillig_instr: {:?}", - &brillig.bytecode[i] - ); - } // next Brillig pc will map to an AVM pc offset by the // number of AVM instructions generated for this Brillig one pc_map[i + 1] = pc_map[i] + num_avm_instrs_for_this_brillig_instr; From 35307f2e36ac37ba837d18d1155239af09a1c9d8 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:02:24 +0000 Subject: [PATCH 8/9] cleanup --- avm-transpiler/src/transpile.rs | 35 +++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 4beb63a6e7b3..a3ae9ac343c2 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -236,14 +236,22 @@ fn handle_foreign_call( ) { match function.as_str() { "keccak256" | "sha256" => { - handle_2_field_hash_instruction(avm_instrs, function, destinations, inputs) + emit_2_field_hash_instruction(avm_instrs, function, destinations, inputs) } - "poseidon" => handle_field_hash_instruction(avm_instrs, function, destinations, inputs), + "poseidon" => emit_single_field_hash_instruction(avm_instrs, function, destinations, inputs), _ => handle_getter_instruction(avm_instrs, function, destinations, inputs), } } -fn handle_2_field_hash_instruction( +/// Two field hash instructions represent instruction's that's outputs are larger than a field element +/// +/// This includes: +/// - keccak +/// - sha256 +/// +/// In the future the output of these may expand / contract depending on what is most efficient for the circuit +/// to reason about. In order to decrease user friction we will use two field outputs. +fn emit_2_field_hash_instruction( avm_instrs: &mut Vec, function: &String, destinations: &[ValueOrArray], @@ -294,7 +302,16 @@ fn handle_2_field_hash_instruction( }); } -fn handle_field_hash_instruction( +/// A single field hash instruction includes hash functions that emit a single field element +/// directly onto the stack. +/// +/// This includes (snark friendly functions): +/// - poseidon2 +/// +/// Pedersen is not implemented this way as the black box function representation has the correct api. +/// As the Poseidon BBF only deals with a single permutation, it is not quite suitable for our current avm +/// representation. +fn emit_single_field_hash_instruction( avm_instrs: &mut Vec, function: &String, destinations: &[ValueOrArray], @@ -340,6 +357,14 @@ fn handle_field_hash_instruction( }); } +/// Getter Instructions are instructions that take NO inputs, and return information +/// from the current execution context. +/// +/// This includes: +/// - Global variables +/// - Caller +/// - storage address +/// - ... fn handle_getter_instruction( avm_instrs: &mut Vec, function: &String, @@ -467,6 +492,8 @@ fn emit_mov(indirect: Option, source: u32, dest: u32) -> AvmInstruction { } } +/// Black box functions, for the meantime only covers pedersen operations as the blackbox function api suits our current needs. +/// (array goes in -> field element comes out) fn handle_black_box_function(avm_instrs: &mut Vec, operation: &BlackBoxOp) { match operation { BlackBoxOp::PedersenHash { From 9ab35ec308a90147ec3dacc4fe315c8e56e82e9b Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:04:33 +0000 Subject: [PATCH 9/9] sweep --- avm-transpiler/src/transpile.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index a3ae9ac343c2..5dfac47fe6ac 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -238,19 +238,21 @@ fn handle_foreign_call( "keccak256" | "sha256" => { emit_2_field_hash_instruction(avm_instrs, function, destinations, inputs) } - "poseidon" => emit_single_field_hash_instruction(avm_instrs, function, destinations, inputs), + "poseidon" => { + emit_single_field_hash_instruction(avm_instrs, function, destinations, inputs) + } _ => handle_getter_instruction(avm_instrs, function, destinations, inputs), } } /// Two field hash instructions represent instruction's that's outputs are larger than a field element -/// +/// /// This includes: /// - keccak /// - sha256 /// /// In the future the output of these may expand / contract depending on what is most efficient for the circuit -/// to reason about. In order to decrease user friction we will use two field outputs. +/// to reason about. In order to decrease user friction we will use two field outputs. fn emit_2_field_hash_instruction( avm_instrs: &mut Vec, function: &String, @@ -302,14 +304,14 @@ fn emit_2_field_hash_instruction( }); } -/// A single field hash instruction includes hash functions that emit a single field element +/// A single field hash instruction includes hash functions that emit a single field element /// directly onto the stack. /// /// This includes (snark friendly functions): /// - poseidon2 /// -/// Pedersen is not implemented this way as the black box function representation has the correct api. -/// As the Poseidon BBF only deals with a single permutation, it is not quite suitable for our current avm +/// Pedersen is not implemented this way as the black box function representation has the correct api. +/// As the Poseidon BBF only deals with a single permutation, it is not quite suitable for our current avm /// representation. fn emit_single_field_hash_instruction( avm_instrs: &mut Vec, @@ -357,13 +359,13 @@ fn emit_single_field_hash_instruction( }); } -/// Getter Instructions are instructions that take NO inputs, and return information -/// from the current execution context. -/// +/// Getter Instructions are instructions that take NO inputs, and return information +/// from the current execution context. +/// /// This includes: /// - Global variables /// - Caller -/// - storage address +/// - storage address /// - ... fn handle_getter_instruction( avm_instrs: &mut Vec, @@ -492,7 +494,7 @@ fn emit_mov(indirect: Option, source: u32, dest: u32) -> AvmInstruction { } } -/// Black box functions, for the meantime only covers pedersen operations as the blackbox function api suits our current needs. +/// Black box functions, for the meantime only covers pedersen operations as the blackbox function api suits our current needs. /// (array goes in -> field element comes out) fn handle_black_box_function(avm_instrs: &mut Vec, operation: &BlackBoxOp) { match operation {