From 93ff6a42c5bc52a2b58bb6e78d0d2c35e05c7349 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 2 Feb 2024 23:21:39 +0000 Subject: [PATCH 1/7] feat(avm): access execution environment within noir contracts --- avm-transpiler/src/transpile.rs | 28 ++++++++++++++++++- .../src/avm/avm_simulator.test.ts | 27 ++++++++++++++++++ yarn-project/aztec-nr/aztec/src/avm.nr | 2 ++ .../aztec-nr/aztec/src/avm/context.nr | 22 +++++++++++++++ .../aztec-nr/aztec/src/avm/env_getters.nr | 5 ++++ yarn-project/aztec-nr/aztec/src/lib.nr | 1 + .../contracts/avm_test_contract/src/main.nr | 8 ++++++ 7 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 yarn-project/aztec-nr/aztec/src/avm.nr create mode 100644 yarn-project/aztec-nr/aztec/src/avm/context.nr create mode 100644 yarn-project/aztec-nr/aztec/src/avm/env_getters.nr diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 04af018da1d6..d9abb8775d47 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}; +use acvm::brillig_vm::brillig::{BinaryFieldOp, BinaryIntOp, ValueOrArray}; use crate::instructions::{ AvmInstruction, AvmOperand, AvmTypeTag, FIRST_OPERAND_INDIRECT, ZEROTH_OPERAND_INDIRECT, @@ -252,6 +252,32 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec { ], ..Default::default() }); + }, + BrilligOpcode::ForeignCall { function, destinations, inputs } => { + println!("{}", function); + match function.as_str() { + "address" => { + assert!(inputs.len() == 0); + 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!("ForeignCall address destination should be a single value"), + }; + + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::ADDRESS, + indirect: Some(0), + operands: vec![ + AvmOperand::U32 { value: dest_offset as u32}, + ], + ..Default::default() + }); + } + _ => panic!("Transpiler doesn't know how to process ForeignCall function {:?}", function), + } + println!("{:?}", destinations); + println!("{:?}", inputs); } _ => panic!( "Transpiler doesn't know how to process {:?} brillig instruction", diff --git a/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts index 62a2c1ab56d0..59758c5aad2b 100644 --- a/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts @@ -1,3 +1,4 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { AvmTestContractArtifact } from '@aztec/noir-contracts'; @@ -8,8 +9,16 @@ import { AvmSimulator } from './avm_simulator.js'; import { initContext, initExecutionEnvironment } from './fixtures/index.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; import { encodeToBytecode } from './serialization/bytecode_serialization.js'; +import { MockProxy, mock } from 'jest-mock-extended'; +import { AvmWorldStateJournal } from './journal/journal.js'; describe('avm', () => { + let journal: MockProxy; + + beforeEach(() => { + journal = mock(); + }); + it('Should execute bytecode that performs basic addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; @@ -54,5 +63,23 @@ describe('avm', () => { expect(returnData.length).toBe(1); expect(returnData).toEqual([new Fr(3)]); }); + + it('Should execute contract function that returns data from the environment', async () => { + const getterArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_accessEnv'); + + // Decode + const instructions = decodeFromBytecode(Buffer.from(getterArtifact!.bytecode, 'base64')); + + // Execute + const address = AztecAddress.fromField(new Fr(1234n)); + const context = new AvmMachineState(initExecutionEnvironment({ address })); + const avmReturnData = await executeAvm(context, journal, instructions); + + expect(avmReturnData.reverted).toBe(false); + + const returnData = avmReturnData.output; + expect(returnData.length).toBe(1); + expect(returnData).toEqual([address]); + }); }); }); diff --git a/yarn-project/aztec-nr/aztec/src/avm.nr b/yarn-project/aztec-nr/aztec/src/avm.nr new file mode 100644 index 000000000000..42deea963351 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/avm.nr @@ -0,0 +1,2 @@ +mod context; +mod env_getters; diff --git a/yarn-project/aztec-nr/aztec/src/avm/context.nr b/yarn-project/aztec-nr/aztec/src/avm/context.nr new file mode 100644 index 000000000000..5307bd6a5ce7 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/avm/context.nr @@ -0,0 +1,22 @@ +use crate::avm::env_getters::{ + get_address, + get_storage_address +}; + +// Getters that will be converted by the transpiler into their +// own opcodes +struct AvmContext {} + +impl AvmContext { + pub fn new() -> Self { + Self {} + } + + pub fn address() -> Field { + get_address() + } + + pub fn storage_address() -> Field { + get_storage_address() + } +} diff --git a/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr b/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr new file mode 100644 index 000000000000..1600e38d3885 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr @@ -0,0 +1,5 @@ +#[oracle(address)] +unconstrained pub fn get_address() -> Field {} + +#[oracle(storage_address)] +unconstrained pub fn get_storage_address() -> Field {} diff --git a/yarn-project/aztec-nr/aztec/src/lib.nr b/yarn-project/aztec-nr/aztec/src/lib.nr index 7a8531601c4f..88866fb2fbcf 100644 --- a/yarn-project/aztec-nr/aztec/src/lib.nr +++ b/yarn-project/aztec-nr/aztec/src/lib.nr @@ -1,4 +1,5 @@ mod abi; +mod avm; mod context; mod hash; mod history; diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr index 28dbf5576d07..963f176aa847 100644 --- a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -6,6 +6,9 @@ contract AvmTest { address::AztecAddress, }; + // avm lib + use dep::aztec::avm::context::AvmContext; + #[aztec(private)] fn constructor() {} @@ -15,6 +18,11 @@ contract AvmTest { argA + argB } + #[aztec(public-vm)] + fn accessEnv() -> pub Field { + AvmContext::address() + } + // Function required for all contracts unconstrained fn compute_note_hash_and_nullifier( _contract_address: AztecAddress, From e9c077afb8fc2dec47a1a023502659bd0c15096f Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sat, 3 Feb 2024 00:17:25 +0000 Subject: [PATCH 2/7] feat: add tests for all env types --- avm-transpiler/src/transpile.rs | 69 +++++++++----- .../src/avm/avm_simulator.test.ts | 94 ++++++++++++++++--- .../aztec-nr/aztec/src/avm/context.nr | 80 ++++++++++++++-- .../aztec-nr/aztec/src/avm/env_getters.nr | 50 +++++++++- .../contracts/avm_test_contract/src/main.nr | 80 +++++++++++++++- 5 files changed, 323 insertions(+), 50 deletions(-) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index d9abb8775d47..1465c5ce9c51 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -254,32 +254,9 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec { }); }, BrilligOpcode::ForeignCall { function, destinations, inputs } => { - println!("{}", function); - match function.as_str() { - "address" => { - assert!(inputs.len() == 0); - 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!("ForeignCall address destination should be a single value"), - }; - - avm_instrs.push(AvmInstruction { - opcode: AvmOpcode::ADDRESS, - indirect: Some(0), - operands: vec![ - AvmOperand::U32 { value: dest_offset as u32}, - ], - ..Default::default() - }); - } - _ => panic!("Transpiler doesn't know how to process ForeignCall function {:?}", function), - } - println!("{:?}", destinations); - println!("{:?}", inputs); + handle_foreign_call(&mut avm_instrs, function, destinations, inputs); } - _ => panic!( + _ => panic!( "Transpiler doesn't know how to process {:?} brillig instruction", brillig_instr ), @@ -296,6 +273,48 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec { bytecode } +fn handle_foreign_call(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.len() == 0); + 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!("ForeignCall address destination should be a single value"), + }; + + let opcode = match function.as_str() { + "address" => AvmOpcode::ADDRESS, + "storageAddress" => AvmOpcode::STORAGEADDRESS, + "origin" => AvmOpcode::ORIGIN, + "sender" => AvmOpcode::SENDER, + "portal" => AvmOpcode::PORTAL, + "feePerL1Gas" => AvmOpcode::FEEPERL1GAS, + "feePerL2Gas" => AvmOpcode::FEEPERL2GAS, + "feePerDaGas" => AvmOpcode::FEEPERDAGAS, + "chainId" => AvmOpcode::CHAINID, + "version" => AvmOpcode::VERSION, + "blockNumber" => AvmOpcode::BLOCKNUMBER, + "timestamp" => AvmOpcode::TIMESTAMP, + // "isStaticCall" => AvmOpcode::ISSTATICCALL, + // "isDelegateCall" => AvmOpcode::ISDELEGATECALL, + _ => panic!("Transpiler doesn't know how to process ForeignCall function {:?}", function), + + }; + + avm_instrs.push(AvmInstruction { + opcode, + indirect: Some(0), + operands: vec![ + AvmOperand::U32 { value: dest_offset as u32}, + ], + ..Default::default() + }); + + +} + + /// 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/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts index 59758c5aad2b..1ff99a379d79 100644 --- a/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts @@ -8,9 +8,11 @@ import { TypeTag } from './avm_memory_types.js'; import { AvmSimulator } from './avm_simulator.js'; import { initContext, initExecutionEnvironment } from './fixtures/index.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; -import { encodeToBytecode } from './serialization/bytecode_serialization.js'; import { MockProxy, mock } from 'jest-mock-extended'; import { AvmWorldStateJournal } from './journal/journal.js'; +import { decodeFromBytecode, encodeToBytecode } from './serialization/bytecode_serialization.js'; +import { AvmMachineState } from './avm_machine_state.js'; +import { EthAddress } from '@aztec/foundation/eth-address'; describe('avm', () => { let journal: MockProxy; @@ -42,6 +44,7 @@ describe('avm', () => { }); describe('testing transpiled Noir contracts', () => { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/4361): sync wire format w/transpiler. it('Should execute contract function that performs addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; @@ -64,22 +67,89 @@ describe('avm', () => { expect(returnData).toEqual([new Fr(3)]); }); - it('Should execute contract function that returns data from the environment', async () => { - const getterArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_accessEnv'); + describe("Test env getters from noir contract", () => { + + const testEnvGetter = async (valueName: string, value: any, functionName: string) => { + const getterArtifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!; - // Decode - const instructions = decodeFromBytecode(Buffer.from(getterArtifact!.bytecode, 'base64')); + // Execute + const overrides = {[valueName]: value}; + const context = initContext({ env: initExecutionEnvironment(overrides) }); - // Execute - const address = AztecAddress.fromField(new Fr(1234n)); - const context = new AvmMachineState(initExecutionEnvironment({ address })); - const avmReturnData = await executeAvm(context, journal, instructions); + // Decode bytecode into instructions + const bytecode = Buffer.from(getterArtifact.bytecode, 'base64'); + jest.spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); - expect(avmReturnData.reverted).toBe(false); + const results = await new AvmSimulator(context).execute(); - const returnData = avmReturnData.output; + expect(results.reverted).toBe(false); + + const returnData = results.output; expect(returnData.length).toBe(1); - expect(returnData).toEqual([address]); + expect(returnData).toEqual([value.toField()]); + + } + + it('address', async () => { + const address = AztecAddress.fromField(new Fr(1)); + await testEnvGetter('address', address, 'avm_getAddress'); + }); + + it('storageAddress', async () => { + const storageAddress = AztecAddress.fromField(new Fr(1)); + await testEnvGetter('storageAddress', storageAddress, 'avm_getStorageAddress'); + }); + + it('sender', async () => { + const sender = AztecAddress.fromField(new Fr(1)); + await testEnvGetter('sender', sender, 'avm_getSender'); }); + + it('origin', async () => { + const origin = AztecAddress.fromField(new Fr(1)); + await testEnvGetter('origin', origin, 'avm_getOrigin'); + }); + + it('portal', async () => { + const portal = EthAddress.fromField(new Fr(1)); + await testEnvGetter('portal', portal, 'avm_getPortal'); + }); + + it('getFeePerL1Gas', async () => { + const fee = new Fr(1); + await testEnvGetter('feePerL1Gas', fee, 'avm_getFeePerL1Gas'); + }); + + it('getFeePerL2Gas', async () => { + const fee = new Fr(1); + await testEnvGetter('feePerL2Gas', fee, 'avm_getFeePerL2Gas'); + }); + + it('getFeePerDaGas', async () => { + const fee = new Fr(1); + await testEnvGetter('feePerDaGas', fee, 'avm_getFeePerDaGas'); + }); + + // it('chainId', async () => { + // const chainId = new Fr(1); + // await testEnvGetter('chainId', chainId, 'avm_getChainId'); + // }); + + // it('version', async () => { + // const version = new Fr(1); + // await testEnvGetter('version', version, 'avm_getVersion'); + // }); + + // it('blockNumber', async () => { + // const blockNumber = new Fr(1); + // await testEnvGetter('blockNumber', blockNumber, 'avm_getBlockNumber'); + // }); + + // it('timestamp', async () => { + // const timestamp = new Fr(1); + // await testEnvGetter('timestamp', timestamp, 'avm_getTimestamp'); + // }); + }); + }); }); diff --git a/yarn-project/aztec-nr/aztec/src/avm/context.nr b/yarn-project/aztec-nr/aztec/src/avm/context.nr index 5307bd6a5ce7..29011a909865 100644 --- a/yarn-project/aztec-nr/aztec/src/avm/context.nr +++ b/yarn-project/aztec-nr/aztec/src/avm/context.nr @@ -1,22 +1,88 @@ use crate::avm::env_getters::{ get_address, - get_storage_address + get_storage_address, + get_origin, + get_sender, + get_portal, + get_fee_per_l1_gas, + get_fee_per_l2_gas, + get_fee_per_da_gas, + get_chain_id, + get_version, + get_block_number, + get_timestamp, + get_is_static_call, + get_is_delegate_call, +}; + +use dep::protocol_types::address::{ + AztecAddress, + EthAddress, }; // Getters that will be converted by the transpiler into their // own opcodes struct AvmContext {} +// No new function as this struct is entirely static getters impl AvmContext { - pub fn new() -> Self { - Self {} - } - - pub fn address() -> Field { + pub fn address() -> AztecAddress { get_address() } - pub fn storage_address() -> Field { + pub fn storage_address() -> AztecAddress { get_storage_address() } + + pub fn origin() -> AztecAddress { + get_origin() + } + + pub fn sender() -> AztecAddress { + get_sender() + } + + pub fn portal() -> EthAddress { + get_portal() + } + + pub fn fee_per_l1_gas() -> Field { + get_fee_per_l1_gas() + } + + pub fn fee_per_l2_gas() -> Field { + get_fee_per_l2_gas() + } + + pub fn fee_per_da_gas() -> Field { + get_fee_per_da_gas() + } + + pub fn chain_id() -> Field { + get_chain_id() + } + + pub fn version() -> Field { + get_version() + } + + pub fn block_number() -> Field { + get_block_number() + } + + pub fn timestamp() -> Field { + get_timestamp() + } + + // pub fn is_static_call() -> Field { + // get_is_static_call() + // } + + // pub fn is_delegate_call() -> Field { + // get_is_delegate_call() + // } + + // pub fn contract_call_depth() -> Field { + // get_contract_call_depth() + // } } diff --git a/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr b/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr index 1600e38d3885..8d491e567238 100644 --- a/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr +++ b/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr @@ -1,5 +1,49 @@ +use dep::protocol_types::address::{ + AztecAddress, + EthAddress, +}; + #[oracle(address)] -unconstrained pub fn get_address() -> Field {} +unconstrained pub fn get_address() -> AztecAddress {} + +#[oracle(storageAddress)] +unconstrained pub fn get_storage_address() -> AztecAddress {} + +#[oracle(origin)] +unconstrained pub fn get_origin() -> AztecAddress {} + +#[oracle(sender)] +unconstrained pub fn get_sender() -> AztecAddress {} + +#[oracle(portal)] +unconstrained pub fn get_portal() -> EthAddress {} + +#[oracle(feePerL1Gas)] +unconstrained pub fn get_fee_per_l1_gas() -> Field {} + +#[oracle(feePerL2Gas)] +unconstrained pub fn get_fee_per_l2_gas() -> Field {} + +#[oracle(feePerDaGas)] +unconstrained pub fn get_fee_per_da_gas() -> Field {} + +#[oracle(chainId)] +unconstrained pub fn get_chain_id() -> Field {} + +#[oracle(version)] +unconstrained pub fn get_version() -> Field {} + +#[oracle(blockNumber)] +unconstrained pub fn get_block_number() -> Field {} + +#[oracle(timestamp)] +unconstrained pub fn get_timestamp() -> Field {} + +#[oracle(isStaticCall)] +unconstrained pub fn get_is_static_call() -> Field {} + +#[oracle(isDelegateCall)] +unconstrained pub fn get_is_delegate_call() -> Field {} -#[oracle(storage_address)] -unconstrained pub fn get_storage_address() -> Field {} +// #[oracle(contractCallDepth)] +// unconstrained pub fn get_contract_call_depth() -> Field {} \ No newline at end of file diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr index 963f176aa847..3b3400f173ef 100644 --- a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -2,8 +2,9 @@ contract AvmTest { // Libs - use dep::aztec::protocol_types::{ - address::AztecAddress, + use dep::aztec::protocol_types::address::{ + AztecAddress, + EthAddress, }; // avm lib @@ -18,11 +19,84 @@ contract AvmTest { argA + argB } + /************************************************************************ + * AvmContext functions + ************************************************************************/ #[aztec(public-vm)] - fn accessEnv() -> pub Field { + fn getAddress() -> pub AztecAddress { AvmContext::address() } + #[aztec(public-vm)] + fn getStorageAddress() -> pub AztecAddress { + AvmContext::storage_address() + } + + #[aztec(public-vm)] + fn getSender() -> pub AztecAddress { + AvmContext::sender() + } + + #[aztec(public-vm)] + fn getOrigin() -> pub AztecAddress { + AvmContext::origin() + } + + #[aztec(public-vm)] + fn getPortal() -> pub EthAddress { + AvmContext::portal() + } + + #[aztec(public-vm)] + fn getFeePerL1Gas() -> pub Field { + AvmContext::fee_per_l1_gas() + } + + #[aztec(public-vm)] + fn getFeePerL2Gas() -> pub Field { + AvmContext::fee_per_l2_gas() + } + + #[aztec(public-vm)] + fn getFeePerDaGas() -> pub Field { + AvmContext::fee_per_da_gas() + } + + #[aztec(public-vm)] + fn getChainId() -> pub Field { + AvmContext::chain_id() + } + + #[aztec(public-vm)] + fn getVersion() -> pub Field { + AvmContext::version() + } + + #[aztec(public-vm)] + fn getBlockNumber() -> pub Field { + AvmContext::block_number() + } + + #[aztec(public-vm)] + fn getTimestamp() -> pub Field { + AvmContext::timestamp() + } + + // #[aztec(public-vm)] + // fn getIsStaticCall() -> pub Field { + // AvmContext::is_static_call() + // } + + // #[aztec(public-vm)] + // fn getIsDelegateCall() -> pub Field { + // AvmContext::is_delegate_call() + // } + + // #[aztec(public-vm)] + // fn getContractCallDepth() -> pub Field { + // AvmContext::contract_call_depth() + // } + // Function required for all contracts unconstrained fn compute_note_hash_and_nullifier( _contract_address: AztecAddress, From f619cf48ef15ed9ed2490acac413ee28c4406870 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sat, 3 Feb 2024 00:17:51 +0000 Subject: [PATCH 3/7] fmt --- avm-transpiler/src/transpile.rs | 22 +-- .../src/avm/avm_simulator.test.ts | 129 ++++++++---------- 2 files changed, 72 insertions(+), 79 deletions(-) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 1465c5ce9c51..e61858861775 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -273,7 +273,12 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec { bytecode } -fn handle_foreign_call(avm_instrs: &mut Vec, function: &String, destinations: &Vec, inputs: &Vec) { +fn handle_foreign_call( + 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.len() == 0); assert!(destinations.len() == 1); @@ -298,23 +303,22 @@ fn handle_foreign_call(avm_instrs: &mut Vec, function: &String, "timestamp" => AvmOpcode::TIMESTAMP, // "isStaticCall" => AvmOpcode::ISSTATICCALL, // "isDelegateCall" => AvmOpcode::ISDELEGATECALL, - _ => 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(0), - operands: vec![ - AvmOperand::U32 { value: dest_offset as u32}, - ], + operands: vec![AvmOperand::U32 { + value: dest_offset as u32, + }], ..Default::default() }); - - } - /// 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/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts index 1ff99a379d79..b72534a8ccc3 100644 --- a/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts @@ -3,23 +3,15 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmTestContractArtifact } from '@aztec/noir-contracts'; import { jest } from '@jest/globals'; +import { EthAddress } from '@aztec/foundation/eth-address'; import { TypeTag } from './avm_memory_types.js'; import { AvmSimulator } from './avm_simulator.js'; import { initContext, initExecutionEnvironment } from './fixtures/index.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; -import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmWorldStateJournal } from './journal/journal.js'; -import { decodeFromBytecode, encodeToBytecode } from './serialization/bytecode_serialization.js'; -import { AvmMachineState } from './avm_machine_state.js'; -import { EthAddress } from '@aztec/foundation/eth-address'; +import {encodeToBytecode } from './serialization/bytecode_serialization.js'; describe('avm', () => { - let journal: MockProxy; - - beforeEach(() => { - journal = mock(); - }); it('Should execute bytecode that performs basic addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; @@ -44,7 +36,6 @@ describe('avm', () => { }); describe('testing transpiled Noir contracts', () => { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/4361): sync wire format w/transpiler. it('Should execute contract function that performs addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; @@ -67,8 +58,7 @@ describe('avm', () => { expect(returnData).toEqual([new Fr(3)]); }); - describe("Test env getters from noir contract", () => { - + describe('Test env getters from noir contract', () => { const testEnvGetter = async (valueName: string, value: any, functionName: string) => { const getterArtifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!; @@ -93,63 +83,62 @@ describe('avm', () => { it('address', async () => { const address = AztecAddress.fromField(new Fr(1)); await testEnvGetter('address', address, 'avm_getAddress'); + }); + + it('storageAddress', async () => { + const storageAddress = AztecAddress.fromField(new Fr(1)); + await testEnvGetter('storageAddress', storageAddress, 'avm_getStorageAddress'); + }); + + it('sender', async () => { + const sender = AztecAddress.fromField(new Fr(1)); + await testEnvGetter('sender', sender, 'avm_getSender'); + }); + + it('origin', async () => { + const origin = AztecAddress.fromField(new Fr(1)); + await testEnvGetter('origin', origin, 'avm_getOrigin'); + }); + + it('portal', async () => { + const portal = EthAddress.fromField(new Fr(1)); + await testEnvGetter('portal', portal, 'avm_getPortal'); + }); + + it('getFeePerL1Gas', async () => { + const fee = new Fr(1); + await testEnvGetter('feePerL1Gas', fee, 'avm_getFeePerL1Gas'); + }); + + it('getFeePerL2Gas', async () => { + const fee = new Fr(1); + await testEnvGetter('feePerL2Gas', fee, 'avm_getFeePerL2Gas'); + }); + + it('getFeePerDaGas', async () => { + const fee = new Fr(1); + await testEnvGetter('feePerDaGas', fee, 'avm_getFeePerDaGas'); + }); + + // it('chainId', async () => { + // const chainId = new Fr(1); + // await testEnvGetter('chainId', chainId, 'avm_getChainId'); + // }); + + // it('version', async () => { + // const version = new Fr(1); + // await testEnvGetter('version', version, 'avm_getVersion'); + // }); + + // it('blockNumber', async () => { + // const blockNumber = new Fr(1); + // await testEnvGetter('blockNumber', blockNumber, 'avm_getBlockNumber'); + // }); + + // it('timestamp', async () => { + // const timestamp = new Fr(1); + // await testEnvGetter('timestamp', timestamp, 'avm_getTimestamp'); + // }); }); - - it('storageAddress', async () => { - const storageAddress = AztecAddress.fromField(new Fr(1)); - await testEnvGetter('storageAddress', storageAddress, 'avm_getStorageAddress'); - }); - - it('sender', async () => { - const sender = AztecAddress.fromField(new Fr(1)); - await testEnvGetter('sender', sender, 'avm_getSender'); - }); - - it('origin', async () => { - const origin = AztecAddress.fromField(new Fr(1)); - await testEnvGetter('origin', origin, 'avm_getOrigin'); - }); - - it('portal', async () => { - const portal = EthAddress.fromField(new Fr(1)); - await testEnvGetter('portal', portal, 'avm_getPortal'); - }); - - it('getFeePerL1Gas', async () => { - const fee = new Fr(1); - await testEnvGetter('feePerL1Gas', fee, 'avm_getFeePerL1Gas'); - }); - - it('getFeePerL2Gas', async () => { - const fee = new Fr(1); - await testEnvGetter('feePerL2Gas', fee, 'avm_getFeePerL2Gas'); - }); - - it('getFeePerDaGas', async () => { - const fee = new Fr(1); - await testEnvGetter('feePerDaGas', fee, 'avm_getFeePerDaGas'); - }); - - // it('chainId', async () => { - // const chainId = new Fr(1); - // await testEnvGetter('chainId', chainId, 'avm_getChainId'); - // }); - - // it('version', async () => { - // const version = new Fr(1); - // await testEnvGetter('version', version, 'avm_getVersion'); - // }); - - // it('blockNumber', async () => { - // const blockNumber = new Fr(1); - // await testEnvGetter('blockNumber', blockNumber, 'avm_getBlockNumber'); - // }); - - // it('timestamp', async () => { - // const timestamp = new Fr(1); - // await testEnvGetter('timestamp', timestamp, 'avm_getTimestamp'); - // }); - }); - }); }); From 8c69cf1062e5727a1708cc4e21cbcdaae70f5b7c Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:18:01 +0000 Subject: [PATCH 4/7] fix: test globals, run write oracles inline --- avm-transpiler/src/transpile.rs | 6 +- barretenberg/cpp/pil/avm/avm.pil | 191 ++++++++++++++++++ .../src/avm/avm_simulator.test.ts | 93 +++++---- yarn-project/aztec-nr/aztec/src/avm.nr | 1 - .../aztec-nr/aztec/src/avm/context.nr | 90 +++------ .../aztec-nr/aztec/src/avm/env_getters.nr | 49 ----- .../contracts/avm_test_contract/src/main.nr | 16 +- 7 files changed, 274 insertions(+), 172 deletions(-) create mode 100644 barretenberg/cpp/pil/avm/avm.pil delete mode 100644 yarn-project/aztec-nr/aztec/src/avm/env_getters.nr diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index e61858861775..13364a9b2ce7 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -273,6 +273,9 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec { bytecode } +/// Handle foreign function calls +/// - Environment getting opcodes will be represented as foreign calls +/// - TODO: support for avm external calls through this function fn handle_foreign_call( avm_instrs: &mut Vec, function: &String, @@ -301,8 +304,7 @@ fn handle_foreign_call( "version" => AvmOpcode::VERSION, "blockNumber" => AvmOpcode::BLOCKNUMBER, "timestamp" => AvmOpcode::TIMESTAMP, - // "isStaticCall" => AvmOpcode::ISSTATICCALL, - // "isDelegateCall" => AvmOpcode::ISDELEGATECALL, + // "callStackDepth" => AvmOpcode::CallStackDepth, _ => panic!( "Transpiler doesn't know how to process ForeignCall function {:?}", function diff --git a/barretenberg/cpp/pil/avm/avm.pil b/barretenberg/cpp/pil/avm/avm.pil new file mode 100644 index 000000000000..26268fe25cd9 --- /dev/null +++ b/barretenberg/cpp/pil/avm/avm.pil @@ -0,0 +1,191 @@ + +include "mem_trace.pil"; +include "alu_chip.pil"; + +namespace avmMini(256); + + //===== CONSTANT POLYNOMIALS ================================================== + pol constant clk(i) { i }; + pol constant first = [1] + [0]*; // Used mostly to toggle off the first row consisting + // only in first element of shifted polynomials. + + //===== CONTROL FLOW ========================================================== + // Program counter + pol commit pc; + // Return Pointer + pol commit internal_return_ptr; + + pol commit sel_internal_call; + pol commit sel_internal_return; + pol commit sel_jump; + + // Halt program execution + pol commit sel_halt; + + //===== TABLE SUBOP-TR ======================================================== + // Boolean selectors for (sub-)operations. Only one operation is activated at + // a time. + + // ADD + pol commit sel_op_add; + // SUB + pol commit sel_op_sub; + // MUL + pol commit sel_op_mul; + // DIV + pol commit sel_op_div; + + // Instruction memory tag (0: uninitialized, 1: u8, 2: u16, 3: u32, 4: u64, 5: u128, 6:field) + pol commit in_tag; + + // Errors + pol commit op_err; // Boolean flag pertaining to an operation error + pol commit tag_err; // Boolean flag (foreign key to memTrace.m_tag_err) + + // A helper witness being the inverse of some value + // to show a non-zero equality + pol commit inv; + + // Intermediate register values + pol commit ia; + pol commit ib; + pol commit ic; + + // Memory operation per intermediate register + pol commit mem_op_a; + pol commit mem_op_b; + pol commit mem_op_c; + + // Read-write flag per intermediate register: Read = 0, Write = 1 + pol commit rwa; + pol commit rwb; + pol commit rwc; + + // Memory index involved into a memory operation per pertaining intermediate register + // We should range constrain it to 32 bits ultimately. For first mini-AVM, + // we will assume that these columns are of the right type. + pol commit mem_idx_a; + pol commit mem_idx_b; + pol commit mem_idx_c; + + + // Track the last line of the execution trace. It does NOT correspond to the last row of the whole table + // of size N. As this depends on the supplied bytecode, this polynomial cannot be constant. + pol commit last; + + // Relations on type constraints + + sel_op_add * (1 - sel_op_add) = 0; + sel_op_sub * (1 - sel_op_sub) = 0; + sel_op_mul * (1 - sel_op_mul) = 0; + sel_op_div * (1 - sel_op_div) = 0; + + sel_internal_call * (1 - sel_internal_call) = 0; + sel_internal_return * (1 - sel_internal_return) = 0; + sel_jump * (1 - sel_jump) = 0; + sel_halt * (1 - sel_halt) = 0; + + op_err * (1 - op_err) = 0; + tag_err * (1 - tag_err) = 0; // Potential optimization (boolean constraint derivation from equivalence check to memTrace)? + + mem_op_a * (1 - mem_op_a) = 0; + mem_op_b * (1 - mem_op_b) = 0; + mem_op_c * (1 - mem_op_c) = 0; + + rwa * (1 - rwa) = 0; + rwb * (1 - rwb) = 0; + rwc * (1 - rwc) = 0; + + // TODO: Constrain rwa, rwb, rwc to u32 type and 0 <= in_tag <= 6 + + // Set intermediate registers to 0 whenever tag_err occurs + tag_err * ia = 0; + tag_err * ib = 0; + tag_err * ic = 0; + + // Relation for division over the finite field + // If tag_err == 1 in a division, then ib == 0 and op_err == 1. + #[SUBOP_DIVISION_FF] + sel_op_div * (1 - op_err) * (ic * ib - ia) = 0; + + // When sel_op_div == 1, we want ib == 0 <==> op_err == 1 + // This can be achieved with the 2 following relations. + // inv is an extra witness to show that we can invert ib, i.e., inv = ib^(-1) + // If ib == 0, we have to set inv = 1 to satisfy the second relation, + // because op_err == 1 from the first relation. + #[SUBOP_DIVISION_ZERO_ERR1] + sel_op_div * (ib * inv - 1 + op_err) = 0; + #[SUBOP_DIVISION_ZERO_ERR2] + sel_op_div * op_err * (1 - inv) = 0; + + // op_err cannot be maliciously activated for a non-relevant + // operation selector, i.e., op_err == 1 ==> sel_op_div || sel_op_XXX || ... + // op_err * (sel_op_div + sel_op_XXX + ... - 1) == 0 + // Note that the above is even a stronger constraint, as it shows + // that exactly one sel_op_XXX must be true. + // At this time, we have only division producing an error. + #[SUBOP_ERROR_RELEVANT_OP] + op_err * (sel_op_div - 1) = 0; + + // TODO: constraint that we stop execution at the first error (tag_err or op_err) + // An error can only happen at the last sub-operation row. + + // OPEN/POTENTIAL OPTIMIZATION: Dedicated error per relevant operation? + // For the division, we could lower the degree from 4 to 3 + // (sel_op_div - op_div_err) * (ic * ib - ia) = 0; + // Same for the relations related to the error activation: + // (ib * inv - 1 + op_div_err) = 0 && op_err * (1 - inv) = 0 + // This works in combination with op_div_err * (sel_op_div - 1) = 0; + // Drawback is the need to paralllelize the latter. + + //===== CONTROL FLOW ======================================================= + //===== JUMP =============================================================== + sel_jump * (pc' - ia) = 0; + + //===== INTERNAL_CALL ====================================================== + // - The program counter in the next row should be equal to the value loaded from the ia register + // - We then write the return location (pc + 1) into the call stack (in memory) + + #[RETURN_POINTER_INCREMENT] + sel_internal_call * (internal_return_ptr' - (internal_return_ptr + 1)) = 0; + sel_internal_call * (internal_return_ptr - mem_idx_b) = 0; + sel_internal_call * (pc' - ia) = 0; + sel_internal_call * ((pc + 1) - ib) = 0; + + // TODO(md): Below relations may be removed through sub-op table lookup + sel_internal_call * (rwb - 1) = 0; + sel_internal_call * (mem_op_b - 1) = 0; + + //===== INTERNAL_RETURN =================================================== + // - We load the memory pointer to be the internal_return_ptr + // - Constrain then next program counter to be the loaded value + // - decrement the internal_return_ptr + + #[RETURN_POINTER_DECREMENT] + sel_internal_return * (internal_return_ptr' - (internal_return_ptr - 1)) = 0; + sel_internal_return * ((internal_return_ptr - 1) - mem_idx_a) = 0; + sel_internal_return * (pc' - ia) = 0; + + // TODO(md): Below relations may be removed through sub-op table lookup + sel_internal_return * rwa = 0; + sel_internal_return * (mem_op_a - 1) = 0; + + //===== CONTROL_FLOW_CONSISTENCY ============================================ + pol INTERNAL_CALL_STACK_SELECTORS = (first + sel_internal_call + sel_internal_return + sel_halt); + pol OPCODE_SELECTORS = (sel_op_add + sel_op_sub + sel_op_div + sel_op_mul); + + // Program counter must increment if not jumping or returning + #[PC_INCREMENT] + (1 - first) * (1 - sel_halt) * OPCODE_SELECTORS * (pc' - (pc + 1)) = 0; + + // first == 0 && sel_internal_call == 0 && sel_internal_return == 0 && sel_halt == 0 ==> internal_return_ptr == internal_return_ptr' + #[INTERNAL_RETURN_POINTER_CONSISTENCY] + (1 - INTERNAL_CALL_STACK_SELECTORS) * (internal_return_ptr' - internal_return_ptr) = 0; + + // TODO: we want to set an initial number for the reserved memory of the jump pointer + + // Inter-table Constraints + + // TODO: tag_err {clk} IS memTrace.m_tag_err {memTrace.m_clk} + // TODO: Map memory trace with intermediate register values whenever there is no tag error, sthg like: + // mem_op_a * (1 - tag_err) {mem_idx_a, clk, ia, rwa} IS m_sub_clk == 0 && 1 - m_tag_err {m_addr, m_clk, m_val, m_rw} diff --git a/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts index b72534a8ccc3..e2231ede872c 100644 --- a/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts @@ -1,18 +1,17 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { AvmTestContractArtifact } from '@aztec/noir-contracts'; import { jest } from '@jest/globals'; -import { EthAddress } from '@aztec/foundation/eth-address'; import { TypeTag } from './avm_memory_types.js'; import { AvmSimulator } from './avm_simulator.js'; -import { initContext, initExecutionEnvironment } from './fixtures/index.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 { encodeToBytecode } from './serialization/bytecode_serialization.js'; describe('avm', () => { - it('Should execute bytecode that performs basic addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; @@ -59,26 +58,34 @@ describe('avm', () => { }); describe('Test env getters from noir contract', () => { - const testEnvGetter = async (valueName: string, value: any, functionName: string) => { - const getterArtifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!; - - // Execute - const overrides = {[valueName]: value}; - const context = initContext({ env: initExecutionEnvironment(overrides) }); - - // Decode bytecode into instructions - const bytecode = Buffer.from(getterArtifact.bytecode, 'base64'); - 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.length).toBe(1); - expect(returnData).toEqual([value.toField()]); - - } + const testEnvGetter = async (valueName: string, value: any, functionName: string, globalVar: boolean = false) => { + const getterArtifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!; + + // Execute + let overrides = {}; + if (globalVar === true) { + const globals = initGlobalVariables({ [valueName]: value }); + overrides = { globals }; + } else { + overrides = { [valueName]: value }; + } + const context = initContext({ env: initExecutionEnvironment(overrides) }); + + // Decode bytecode into instructions + const bytecode = Buffer.from(getterArtifact.bytecode, 'base64'); + jest + .spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode') + .mockReturnValue(Promise.resolve(bytecode)); + // Execute + + const results = await new AvmSimulator(context).execute(); + + expect(results.reverted).toBe(false); + + const returnData = results.output; + expect(returnData.length).toBe(1); + expect(returnData).toEqual([value.toField()]); + }; it('address', async () => { const address = AztecAddress.fromField(new Fr(1)); @@ -120,25 +127,25 @@ describe('avm', () => { await testEnvGetter('feePerDaGas', fee, 'avm_getFeePerDaGas'); }); - // it('chainId', async () => { - // const chainId = new Fr(1); - // await testEnvGetter('chainId', chainId, 'avm_getChainId'); - // }); - - // it('version', async () => { - // const version = new Fr(1); - // await testEnvGetter('version', version, 'avm_getVersion'); - // }); - - // it('blockNumber', async () => { - // const blockNumber = new Fr(1); - // await testEnvGetter('blockNumber', blockNumber, 'avm_getBlockNumber'); - // }); - - // it('timestamp', async () => { - // const timestamp = new Fr(1); - // await testEnvGetter('timestamp', timestamp, 'avm_getTimestamp'); - // }); + it('chainId', async () => { + const chainId = new Fr(1); + await testEnvGetter('chainId', chainId, 'avm_getChainId', /*globalVar=*/ true); + }); + + it('version', async () => { + const version = new Fr(1); + await testEnvGetter('version', version, 'avm_getVersion', /*globalVar=*/ true); + }); + + it('blockNumber', async () => { + const blockNumber = new Fr(1); + await testEnvGetter('blockNumber', blockNumber, 'avm_getBlockNumber', /*globalVar=*/ true); + }); + + it('timestamp', async () => { + const timestamp = new Fr(1); + await testEnvGetter('timestamp', timestamp, 'avm_getTimestamp', /*globalVar=*/ true); + }); }); }); }); diff --git a/yarn-project/aztec-nr/aztec/src/avm.nr b/yarn-project/aztec-nr/aztec/src/avm.nr index 42deea963351..3d9885db80d8 100644 --- a/yarn-project/aztec-nr/aztec/src/avm.nr +++ b/yarn-project/aztec-nr/aztec/src/avm.nr @@ -1,2 +1 @@ mod context; -mod env_getters; diff --git a/yarn-project/aztec-nr/aztec/src/avm/context.nr b/yarn-project/aztec-nr/aztec/src/avm/context.nr index 29011a909865..4e6d3f56f214 100644 --- a/yarn-project/aztec-nr/aztec/src/avm/context.nr +++ b/yarn-project/aztec-nr/aztec/src/avm/context.nr @@ -1,20 +1,3 @@ -use crate::avm::env_getters::{ - get_address, - get_storage_address, - get_origin, - get_sender, - get_portal, - get_fee_per_l1_gas, - get_fee_per_l2_gas, - get_fee_per_da_gas, - get_chain_id, - get_version, - get_block_number, - get_timestamp, - get_is_static_call, - get_is_delegate_call, -}; - use dep::protocol_types::address::{ AztecAddress, EthAddress, @@ -26,63 +9,42 @@ struct AvmContext {} // No new function as this struct is entirely static getters impl AvmContext { - pub fn address() -> AztecAddress { - get_address() - } - - pub fn storage_address() -> AztecAddress { - get_storage_address() - } - - pub fn origin() -> AztecAddress { - get_origin() - } + #[oracle(address)] + pub fn address() -> AztecAddress {} - pub fn sender() -> AztecAddress { - get_sender() - } + #[oracle(storageAddress)] + pub fn storage_address() -> AztecAddress {} - pub fn portal() -> EthAddress { - get_portal() - } + #[oracle(origin)] + pub fn origin() -> AztecAddress {} - pub fn fee_per_l1_gas() -> Field { - get_fee_per_l1_gas() - } + #[oracle(sender)] + pub fn sender() -> AztecAddress {} - pub fn fee_per_l2_gas() -> Field { - get_fee_per_l2_gas() - } + #[oracle(portal)] + pub fn portal() -> EthAddress {} - pub fn fee_per_da_gas() -> Field { - get_fee_per_da_gas() - } + #[oracle(feePerL1Gas)] + pub fn fee_per_l1_gas() -> Field {} - pub fn chain_id() -> Field { - get_chain_id() - } + #[oracle(feePerL2Gas)] + pub fn fee_per_l2_gas() -> Field {} - pub fn version() -> Field { - get_version() - } + #[oracle(feePerDaGas)] + pub fn fee_per_da_gas() -> Field {} - pub fn block_number() -> Field { - get_block_number() - } + #[oracle(chainId)] + pub fn chain_id() -> Field {} - pub fn timestamp() -> Field { - get_timestamp() - } + #[oracle(version)] + pub fn version() -> Field {} - // pub fn is_static_call() -> Field { - // get_is_static_call() - // } + #[oracle(blockNumber)] + pub fn block_number() -> Field {} - // pub fn is_delegate_call() -> Field { - // get_is_delegate_call() - // } + #[oracle(timestamp)] + pub fn timestamp() -> Field {} - // pub fn contract_call_depth() -> Field { - // get_contract_call_depth() - // } + // #[oracle(contractCallDepth)] + // pub fn contract_call_depth() -> Field {} } diff --git a/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr b/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr deleted file mode 100644 index 8d491e567238..000000000000 --- a/yarn-project/aztec-nr/aztec/src/avm/env_getters.nr +++ /dev/null @@ -1,49 +0,0 @@ -use dep::protocol_types::address::{ - AztecAddress, - EthAddress, -}; - -#[oracle(address)] -unconstrained pub fn get_address() -> AztecAddress {} - -#[oracle(storageAddress)] -unconstrained pub fn get_storage_address() -> AztecAddress {} - -#[oracle(origin)] -unconstrained pub fn get_origin() -> AztecAddress {} - -#[oracle(sender)] -unconstrained pub fn get_sender() -> AztecAddress {} - -#[oracle(portal)] -unconstrained pub fn get_portal() -> EthAddress {} - -#[oracle(feePerL1Gas)] -unconstrained pub fn get_fee_per_l1_gas() -> Field {} - -#[oracle(feePerL2Gas)] -unconstrained pub fn get_fee_per_l2_gas() -> Field {} - -#[oracle(feePerDaGas)] -unconstrained pub fn get_fee_per_da_gas() -> Field {} - -#[oracle(chainId)] -unconstrained pub fn get_chain_id() -> Field {} - -#[oracle(version)] -unconstrained pub fn get_version() -> Field {} - -#[oracle(blockNumber)] -unconstrained pub fn get_block_number() -> Field {} - -#[oracle(timestamp)] -unconstrained pub fn get_timestamp() -> Field {} - -#[oracle(isStaticCall)] -unconstrained pub fn get_is_static_call() -> Field {} - -#[oracle(isDelegateCall)] -unconstrained pub fn get_is_delegate_call() -> Field {} - -// #[oracle(contractCallDepth)] -// unconstrained pub fn get_contract_call_depth() -> Field {} \ No newline at end of file diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr index 3b3400f173ef..107b94940465 100644 --- a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -28,17 +28,17 @@ contract AvmTest { } #[aztec(public-vm)] - fn getStorageAddress() -> pub AztecAddress { + fn getStorageAddress() -> pub AztecAddress { AvmContext::storage_address() } #[aztec(public-vm)] - fn getSender() -> pub AztecAddress { + fn getSender() -> pub AztecAddress { AvmContext::sender() } #[aztec(public-vm)] - fn getOrigin() -> pub AztecAddress { + fn getOrigin() -> pub AztecAddress { AvmContext::origin() } @@ -82,16 +82,6 @@ contract AvmTest { AvmContext::timestamp() } - // #[aztec(public-vm)] - // fn getIsStaticCall() -> pub Field { - // AvmContext::is_static_call() - // } - - // #[aztec(public-vm)] - // fn getIsDelegateCall() -> pub Field { - // AvmContext::is_delegate_call() - // } - // #[aztec(public-vm)] // fn getContractCallDepth() -> pub Field { // AvmContext::contract_call_depth() From 2ea7fb79566691611c555c7c58a9ed40ffb3b68c Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Mon, 5 Feb 2024 23:11:26 +0000 Subject: [PATCH 5/7] feat(avm): bake in avm context with macro --- noir/aztec_macros/src/lib.rs | 16 + yarn-project/aztec-nr/aztec/src/avm.nr | 1 - .../aztec-nr/aztec/src/avm/context.nr | 50 -- yarn-project/aztec-nr/aztec/src/context.nr | 611 +----------------- .../aztec-nr/aztec/src/context/private.nr | 417 ++++++++++++ .../aztec-nr/aztec/src/context/public.nr | 248 +++++++ .../aztec-nr/aztec/src/context/public_vm.nr | 54 ++ yarn-project/aztec-nr/aztec/src/lib.nr | 1 - .../contracts/avm_test_contract/src/main.nr | 29 +- 9 files changed, 754 insertions(+), 673 deletions(-) delete mode 100644 yarn-project/aztec-nr/aztec/src/avm.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/avm/context.nr create mode 100644 yarn-project/aztec-nr/aztec/src/context/private.nr create mode 100644 yarn-project/aztec-nr/aztec/src/context/public.nr create mode 100644 yarn-project/aztec-nr/aztec/src/context/public_vm.nr diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index 228664c83fb9..1f55478338da 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -645,6 +645,10 @@ fn transform_vm_function( func: &mut NoirFunction, _storage_defined: bool, ) -> Result<(), AztecMacroError> { + // Push Avm context creation to the beginning of the function + let create_context = create_avm_context()?; + func.def.body.0.insert(0, create_context); + // We want the function to be seen as a public function func.def.is_open = true; @@ -1158,6 +1162,18 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac Ok(injected_expressions) } +fn create_avm_context() -> Result { + let let_context = mutable_assignment( + "context", // Assigned to + call( + variable_path(chained_path!("aztec", "context", "PublicVmContext", "new")), // Path + vec![], // args + ), + ); + + Ok(let_context) +} + /// Abstract Return Type /// /// This function intercepts the function's current return type and replaces it with pushes diff --git a/yarn-project/aztec-nr/aztec/src/avm.nr b/yarn-project/aztec-nr/aztec/src/avm.nr deleted file mode 100644 index 3d9885db80d8..000000000000 --- a/yarn-project/aztec-nr/aztec/src/avm.nr +++ /dev/null @@ -1 +0,0 @@ -mod context; diff --git a/yarn-project/aztec-nr/aztec/src/avm/context.nr b/yarn-project/aztec-nr/aztec/src/avm/context.nr deleted file mode 100644 index 4e6d3f56f214..000000000000 --- a/yarn-project/aztec-nr/aztec/src/avm/context.nr +++ /dev/null @@ -1,50 +0,0 @@ -use dep::protocol_types::address::{ - AztecAddress, - EthAddress, -}; - -// Getters that will be converted by the transpiler into their -// own opcodes -struct AvmContext {} - -// No new function as this struct is entirely static getters -impl AvmContext { - #[oracle(address)] - pub fn address() -> AztecAddress {} - - #[oracle(storageAddress)] - pub fn storage_address() -> AztecAddress {} - - #[oracle(origin)] - pub fn origin() -> AztecAddress {} - - #[oracle(sender)] - pub fn sender() -> AztecAddress {} - - #[oracle(portal)] - pub fn portal() -> EthAddress {} - - #[oracle(feePerL1Gas)] - pub fn fee_per_l1_gas() -> Field {} - - #[oracle(feePerL2Gas)] - pub fn fee_per_l2_gas() -> Field {} - - #[oracle(feePerDaGas)] - pub fn fee_per_da_gas() -> Field {} - - #[oracle(chainId)] - pub fn chain_id() -> Field {} - - #[oracle(version)] - pub fn version() -> Field {} - - #[oracle(blockNumber)] - pub fn block_number() -> Field {} - - #[oracle(timestamp)] - pub fn timestamp() -> Field {} - - // #[oracle(contractCallDepth)] - // pub fn contract_call_depth() -> Field {} -} diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index 1c0f6ce13893..77fda459f2b0 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -1,609 +1,10 @@ -use crate::{ - abi::{ - PrivateContextInputs, - PublicContextInputs, - }, - key::nullifier_key::validate_nullifier_key_against_address, - messaging::process_l1_to_l2_message, - oracle::{ - arguments, - call_private_function::call_private_function_internal, - public_call::call_public_function_internal, - enqueue_public_function_call::enqueue_public_function_call_internal, - context::get_portal_address, - header::get_header_at, - nullifier_key::{get_nullifier_key_pair, NullifierKeyPair}, - }, -}; -use dep::protocol_types::{ - abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, - call_context::CallContext, - global_variables::GlobalVariables, - function_data::FunctionData, - function_selector::FunctionSelector, - nullifier_key_validation_request::NullifierKeyValidationRequest, - private_call_stack_item::PrivateCallStackItem, - private_circuit_public_inputs::PrivateCircuitPublicInputs, - public_call_stack_item::PublicCallStackItem, - public_circuit_public_inputs::PublicCircuitPublicInputs, - side_effect::{SideEffect, SideEffectLinkedToNoteHash}, - }, - address::{ - AztecAddress, - EthAddress, - }, - constants::{ - MAX_NEW_COMMITMENTS_PER_CALL, - MAX_NEW_L2_TO_L1_MSGS_PER_CALL, - MAX_NEW_NULLIFIERS_PER_CALL, - MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, - MAX_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, - NUM_FIELDS_PER_SHA256, - RETURN_VALUES_LENGTH, - }, - contract_class::ContractClassId, - contrakt::{ - deployment_data::ContractDeploymentData, - storage_read::StorageRead, - storage_update_request::StorageUpdateRequest, - }, - grumpkin_point::GrumpkinPoint, - grumpkin_private_key::GrumpkinPrivateKey, - hash::hash_args, - header::Header, - state_reference::StateReference, - utils::reader::Reader, -}; -use dep::std::{ - option::Option, -}; +mod private; +mod public; +mod public_vm; -// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) -// use dep::std::collections::vec::Vec; - -// When finished, one can call .finish() to convert back to the abi -struct PrivateContext { - // docs:start:private-context - inputs: PrivateContextInputs, - side_effect_counter: u32, - - meta_hwm: u32, - - args_hash : Field, - return_values : BoundedVec, - - read_requests: BoundedVec, - nullifier_key_validation_requests: BoundedVec, - - new_commitments: BoundedVec, - new_nullifiers: BoundedVec, - - private_call_stack_hashes : BoundedVec, - public_call_stack_hashes : BoundedVec, - new_l2_to_l1_msgs : BoundedVec, - // docs:end:private-context - - // Header of a block whose state is used during private execution (not the block the transaction is included in). - historical_header: Header, - - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - // encrypted_logs_preimages: Vec, - // unencrypted_logs_preimages: Vec, - - nullifier_key: Option, -} - -impl PrivateContext { - pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext { - PrivateContext { - inputs: inputs, - side_effect_counter: inputs.call_context.start_side_effect_counter, - meta_hwm: 0, - - args_hash: args_hash, - return_values: BoundedVec::new(0), - - read_requests: BoundedVec::new(SideEffect::empty()), - nullifier_key_validation_requests: BoundedVec::new(NullifierKeyValidationRequest::empty()), - - new_commitments: BoundedVec::new(SideEffect::empty()), - new_nullifiers: BoundedVec::new(SideEffectLinkedToNoteHash::empty()), - - historical_header: inputs.historical_header, - - private_call_stack_hashes: BoundedVec::new(0), - public_call_stack_hashes: BoundedVec::new(0), - new_l2_to_l1_msgs: BoundedVec::new(0), - - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - // encrypted_logs_preimages: Vec::new(), - // unencrypted_logs_preimages: Vec::new(), - - nullifier_key: Option::none(), - } - } - - pub fn msg_sender(self) -> AztecAddress { - self.inputs.call_context.msg_sender - } - - pub fn this_address(self) -> AztecAddress { - self.inputs.call_context.storage_contract_address - } - - pub fn this_portal_address(self) -> EthAddress { - self.inputs.call_context.portal_contract_address - } - - pub fn chain_id(self) -> Field { - self.inputs.private_global_variables.chain_id - } - - pub fn version(self) -> Field { - self.inputs.private_global_variables.version - } - - pub fn selector(self) -> FunctionSelector { - self.inputs.call_context.function_selector - } - - // Returns the header of a block whose state is used during private execution (not the block the transaction is - // included in). - pub fn get_header(self) -> Header { - self.historical_header - } - - // Returns the header of an arbitrary block whose block number is less than or equal to the block number - // of historical header. - pub fn get_header_at(self, block_number: u32) -> Header { - get_header_at(block_number, self) - } - - pub fn finish(self) -> PrivateCircuitPublicInputs { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - let encrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; - let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; - let encrypted_log_preimages_length = 0; - let unencrypted_log_preimages_length = 0; - - let priv_circuit_pub_inputs = PrivateCircuitPublicInputs { - call_context: self.inputs.call_context, - args_hash: self.args_hash, - return_values: self.return_values.storage, - meta_hwm: self.meta_hwm, - read_requests: self.read_requests.storage, - nullifier_key_validation_requests: self.nullifier_key_validation_requests.storage, - new_commitments: self.new_commitments.storage, - new_nullifiers: self.new_nullifiers.storage, - private_call_stack_hashes: self.private_call_stack_hashes.storage, - public_call_stack_hashes: self.public_call_stack_hashes.storage, - new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, - end_side_effect_counter: self.side_effect_counter, - encrypted_logs_hash: encrypted_logs_hash, - unencrypted_logs_hash: unencrypted_logs_hash, - encrypted_log_preimages_length: encrypted_log_preimages_length, - unencrypted_log_preimages_length: unencrypted_log_preimages_length, - historical_header: self.historical_header, - contract_deployment_data: self.inputs.contract_deployment_data, - chain_id: self.inputs.private_global_variables.chain_id, - version: self.inputs.private_global_variables.version, - }; - priv_circuit_pub_inputs - } - - pub fn push_read_request(&mut self, read_request: Field) { - let side_effect = SideEffect { - value: read_request, - counter: self.side_effect_counter, - }; - self.read_requests.push(side_effect); - self.side_effect_counter = self.side_effect_counter + 1; - } - - pub fn push_new_note_hash(&mut self, note_hash: Field) { - let side_effect = SideEffect { - value: note_hash, - counter: self.side_effect_counter, - }; - self.new_commitments.push(side_effect); - self.side_effect_counter = self.side_effect_counter + 1; - } - - pub fn push_new_nullifier(&mut self, nullifier: Field, nullified_commitment: Field) { - let side_effect = SideEffectLinkedToNoteHash { - value: nullifier, - note_hash: nullified_commitment, - counter: self.side_effect_counter, - }; - self.new_nullifiers.push(side_effect); - self.side_effect_counter = self.side_effect_counter + 1; - } - - pub fn request_nullifier_secret_key(&mut self, account: AztecAddress) -> GrumpkinPrivateKey { - let key_pair = if self.nullifier_key.is_none() { - let key_pair = get_nullifier_key_pair(account); - validate_nullifier_key_against_address(account, key_pair.public_key); - let request = NullifierKeyValidationRequest { public_key: key_pair.public_key, secret_key: key_pair.secret_key }; - self.nullifier_key_validation_requests.push(request); - self.nullifier_key = Option::some(key_pair); - key_pair - } else { - let key_pair = self.nullifier_key.unwrap_unchecked(); - // If MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL is larger than 1, need to update the way the key pair is cached. - assert(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL == 1); - assert(key_pair.account == account, "Cannot query nullifier key for more than one account per call"); - key_pair - }; - key_pair.secret_key - } - - // docs:start:context_message_portal - pub fn message_portal(&mut self, content: Field) - // docs:end:context_message_portal - { - self.new_l2_to_l1_msgs.push(content); - } - - // PrivateContextInputs must be temporarily passed in to prevent too many unknowns - // Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned - // docs:start:context_consume_l1_to_l2_message - // docs:start:consume_l1_to_l2_message - pub fn consume_l1_to_l2_message( - &mut self, - msg_key: Field, - content: Field, - secret: Field - ) - // docs:end:context_consume_l1_to_l2_message - { - let nullifier = process_l1_to_l2_message(self.historical_header.state.l1_to_l2_message_tree.root, self.this_address(), self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret); - - // Push nullifier (and the "commitment" corresponding to this can be "empty") - self.push_new_nullifier(nullifier, 0) - } - // docs:end:consume_l1_to_l2_message - - pub fn accumulate_encrypted_logs(&mut self, log: [Field; N]) { - let _void1 = self.inputs; - let _void2 = log; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - } - - pub fn accumulate_unencrypted_logs(&mut self, log: T) { - let _void1 = self.inputs; - let _void2 = log; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - } - - pub fn call_private_function( - &mut self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] - ) -> [Field; RETURN_VALUES_LENGTH] { - let args_hash = hash_args(args); - assert(args_hash == arguments::pack_arguments(args)); - self.call_private_function_with_packed_args(contract_address, function_selector, args_hash) - } - - pub fn call_private_function_no_args( - &mut self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - ) -> [Field; RETURN_VALUES_LENGTH] { - self.call_private_function_with_packed_args(contract_address, function_selector, 0) - } - - pub fn call_private_function_with_packed_args( - &mut self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - args_hash: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let item = call_private_function_internal( - contract_address, - function_selector, - args_hash, - self.side_effect_counter, - ); - - assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter); - self.side_effect_counter = item.public_inputs.end_side_effect_counter + 1; - - assert(contract_address.eq(item.contract_address)); - assert(function_selector.eq(item.function_data.selector)); - - assert(args_hash == item.public_inputs.args_hash); - - // Assert that the call context of the enqueued call generated by the oracle matches our request. - // We are issuing a regular call which is not delegate, static, or deployment. We also constrain - // the msg_sender in the nested call to be equal to our address, and the execution context address - // for the nested call to be equal to the address we actually called. - assert(item.public_inputs.call_context.is_delegate_call == false); - assert(item.public_inputs.call_context.is_static_call == false); - assert(item.public_inputs.call_context.is_contract_deployment == false); - assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)); - assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address)); - - self.private_call_stack_hashes.push(item.hash()); - - item.public_inputs.return_values - } - - pub fn call_public_function( - &mut self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] - ) { - let args_hash = hash_args(args); - assert(args_hash == arguments::pack_arguments(args)); - self.call_public_function_with_packed_args(contract_address, function_selector, args_hash) - } - - pub fn call_public_function_no_args( - &mut self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - ) { - self.call_public_function_with_packed_args(contract_address, function_selector, 0) - } - - pub fn call_public_function_with_packed_args( - &mut self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - args_hash: Field - ) { - let fields = enqueue_public_function_call_internal( - contract_address, - function_selector, - args_hash, - self.side_effect_counter - ); - - let mut reader = Reader::new(fields); - - // Note: Not using PublicCirclePublicInputs::deserialize here, because everything below args_hash is 0 and - // there is no more data in fields because there is only ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE fields! - let item = PublicCallStackItem { - contract_address: AztecAddress::from_field(reader.read()), - function_data: reader.read_struct(FunctionData::deserialize), - public_inputs: PublicCircuitPublicInputs { - call_context: reader.read_struct(CallContext::deserialize), - args_hash: reader.read(), - return_values: [0; RETURN_VALUES_LENGTH], - contract_storage_update_requests: [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL], - contract_storage_reads: [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL], - public_call_stack_hashes: [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], - new_commitments: [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL], - new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL], - new_l2_to_l1_msgs:[0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL], - unencrypted_logs_hash:[0; NUM_FIELDS_PER_SHA256], - unencrypted_log_preimages_length: 0, - historical_header: Header::empty(), - prover_address: AztecAddress::zero(), - }, - is_execution_request: true, - }; - reader.finish(); - - assert(contract_address.eq(item.contract_address)); - assert(function_selector.eq(item.function_data.selector)); - - assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter); - // We increment the sideffect counter by one, to account for the call itself being a side effect. - self.side_effect_counter = self.side_effect_counter + 1; - - assert(args_hash == item.public_inputs.args_hash); - - // Assert that the call context of the enqueued call generated by the oracle matches our request. - // We are issuing a regular call which is not delegate, static, or deployment. We also constrain - // the msg_sender in the nested call to be equal to our address, and the execution context address - // for the nested call to be equal to the address we actually called. - assert(item.public_inputs.call_context.is_delegate_call == false); - assert(item.public_inputs.call_context.is_static_call == false); - assert(item.public_inputs.call_context.is_contract_deployment == false); - assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)); - assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address)); - - self.public_call_stack_hashes.push(item.hash()); - } -} - -struct PublicContext { - inputs: PublicContextInputs, - side_effect_counter: u32, - - args_hash : Field, - return_values : BoundedVec, - - contract_storage_update_requests: BoundedVec, - contract_storage_reads: BoundedVec, - public_call_stack_hashes: BoundedVec, - - new_commitments: BoundedVec, - new_nullifiers: BoundedVec, - - new_l2_to_l1_msgs: BoundedVec, - - unencrypted_logs_hash: BoundedVec, - unencrypted_logs_preimages_length: Field, - - // Header of a block whose state is used during public execution. Set by sequencer to be a header of a block - // previous to the one in which the tx is included. - historical_header: Header, - prover_address: AztecAddress, -} - -impl PublicContext { - pub fn new(inputs: PublicContextInputs, args_hash: Field) -> PublicContext { - let empty_storage_read = StorageRead::empty(); - let empty_storage_update = StorageUpdateRequest::empty(); - PublicContext { - inputs: inputs, - side_effect_counter: inputs.call_context.start_side_effect_counter, - - args_hash: args_hash, - return_values: BoundedVec::new(0), - - contract_storage_update_requests: BoundedVec::new(empty_storage_update), - contract_storage_reads: BoundedVec::new(empty_storage_read), - public_call_stack_hashes: BoundedVec::new(0), - - new_commitments: BoundedVec::new(SideEffect::empty()), - new_nullifiers: BoundedVec::new(SideEffectLinkedToNoteHash::empty()), - - new_l2_to_l1_msgs: BoundedVec::new(0), - - - unencrypted_logs_hash: BoundedVec::new(0), - unencrypted_logs_preimages_length: 0, - - historical_header: inputs.historical_header, - prover_address: AztecAddress::zero(), - - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - // encrypted_logs_preimages: Vec::new(), - // unencrypted_logs_preimages: Vec::new(), - } - } - - pub fn msg_sender(self) -> AztecAddress { - self.inputs.call_context.msg_sender - } - - pub fn this_address(self) -> AztecAddress { - self.inputs.call_context.storage_contract_address - } - - pub fn this_portal_address(self) -> EthAddress { - self.inputs.call_context.portal_contract_address - } - - pub fn chain_id(self) -> Field { - self.inputs.public_global_variables.chain_id - } - - pub fn version(self) -> Field { - self.inputs.public_global_variables.version - } - - pub fn selector(self) -> FunctionSelector { - self.inputs.call_context.function_selector - } - - pub fn block_number(self) -> Field { - self.inputs.public_global_variables.block_number - } - - pub fn timestamp(self) -> Field { - self.inputs.public_global_variables.timestamp - } - - pub fn finish(self) -> PublicCircuitPublicInputs { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; - let unencrypted_log_preimages_length = 0; - - - // Compute the public call stack hashes - let pub_circuit_pub_inputs = PublicCircuitPublicInputs { - call_context: self.inputs.call_context, // Done - args_hash: self.args_hash, // Done - contract_storage_update_requests: self.contract_storage_update_requests.storage, - contract_storage_reads: self.contract_storage_reads.storage, - return_values: self.return_values.storage, - new_commitments: self.new_commitments.storage, - new_nullifiers: self.new_nullifiers.storage, - public_call_stack_hashes: self.public_call_stack_hashes.storage, - new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, - unencrypted_logs_hash: unencrypted_logs_hash, - unencrypted_log_preimages_length: unencrypted_log_preimages_length, - historical_header: self.inputs.historical_header, - prover_address: self.prover_address, - }; - pub_circuit_pub_inputs - } - - pub fn push_new_note_hash(&mut self, note_hash: Field) { - let side_effect = SideEffect { - value: note_hash, - counter: self.side_effect_counter - }; - self.new_commitments.push(side_effect); - self.side_effect_counter = self.side_effect_counter + 1; - } - - pub fn push_new_nullifier(&mut self, nullifier: Field, _nullified_commitment: Field) { - let side_effect = SideEffectLinkedToNoteHash { - value: nullifier, - note_hash: 0, // cannot nullify pending notes in public context - counter: self.side_effect_counter - }; - self.new_nullifiers.push(side_effect); - self.side_effect_counter = self.side_effect_counter + 1; - } - - pub fn message_portal(&mut self, content: Field) { - self.new_l2_to_l1_msgs.push(content); - } - - // PrivateContextInputs must be temporarily passed in to prevent too many unknowns - // Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned - pub fn consume_l1_to_l2_message(&mut self, msg_key: Field, content: Field, secret: Field) { - let this = (*self).this_address(); - let nullifier = process_l1_to_l2_message(self.historical_header.state.l1_to_l2_message_tree.root, this, self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret); - - // Push nullifier (and the "commitment" corresponding to this can be "empty") - self.push_new_nullifier(nullifier, 0) - } - - pub fn accumulate_encrypted_logs(&mut self, log: [Field; N]) { - let _void1 = self; - let _void2 = log; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - } - - pub fn accumulate_unencrypted_logs(&mut self, log: T) { - let _void1 = self; - let _void2 = log; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - } - - pub fn call_public_function( - _self: Self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - args: [Field; ARGS_COUNT], - ) -> [Field; RETURN_VALUES_LENGTH] { - let args_hash = hash_args(args); - assert(args_hash == arguments::pack_arguments(args)); - call_public_function_internal( - contract_address, - function_selector, - args_hash, - ) - } - - pub fn call_public_function_no_args( - _self: Self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - ) -> [Field; RETURN_VALUES_LENGTH] { - call_public_function_internal( - contract_address, - function_selector, - 0, - ) - } - -} +use public::PublicContext; +use private::PrivateContext; +use public_vm::PublicVmContext; struct Context { private: Option<&mut PrivateContext>, diff --git a/yarn-project/aztec-nr/aztec/src/context/private.nr b/yarn-project/aztec-nr/aztec/src/context/private.nr new file mode 100644 index 000000000000..525c05e17e47 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/context/private.nr @@ -0,0 +1,417 @@ +use crate::{ + abi::{ + PrivateContextInputs, + PublicContextInputs, + }, + key::nullifier_key::validate_nullifier_key_against_address, + messaging::process_l1_to_l2_message, + oracle::{ + arguments, + call_private_function::call_private_function_internal, + public_call::call_public_function_internal, + enqueue_public_function_call::enqueue_public_function_call_internal, + context::get_portal_address, + header::get_header_at, + nullifier_key::{get_nullifier_key_pair, NullifierKeyPair}, + }, +}; +use dep::protocol_types::{ + abis::{ + append_only_tree_snapshot::AppendOnlyTreeSnapshot, + call_context::CallContext, + global_variables::GlobalVariables, + function_data::FunctionData, + function_selector::FunctionSelector, + nullifier_key_validation_request::NullifierKeyValidationRequest, + private_call_stack_item::PrivateCallStackItem, + private_circuit_public_inputs::PrivateCircuitPublicInputs, + public_call_stack_item::PublicCallStackItem, + public_circuit_public_inputs::PublicCircuitPublicInputs, + side_effect::{SideEffect, SideEffectLinkedToNoteHash}, + }, + address::{ + AztecAddress, + EthAddress, + }, + constants::{ + MAX_NEW_COMMITMENTS_PER_CALL, + MAX_NEW_L2_TO_L1_MSGS_PER_CALL, + MAX_NEW_NULLIFIERS_PER_CALL, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_DATA_READS_PER_CALL, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, + MAX_READ_REQUESTS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, + NUM_FIELDS_PER_SHA256, + RETURN_VALUES_LENGTH, + }, + contract_class::ContractClassId, + contrakt::{ + deployment_data::ContractDeploymentData, + storage_read::StorageRead, + storage_update_request::StorageUpdateRequest, + }, + grumpkin_point::GrumpkinPoint, + grumpkin_private_key::GrumpkinPrivateKey, + hash::hash_args, + header::Header, + state_reference::StateReference, + utils::reader::Reader, +}; +use dep::std::{ + option::Option, +}; + +// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) +// use dep::std::collections::vec::Vec; + +// When finished, one can call .finish() to convert back to the abi +struct PrivateContext { + // docs:start:private-context + inputs: PrivateContextInputs, + side_effect_counter: u32, + + meta_hwm: u32, + + args_hash : Field, + return_values : BoundedVec, + + read_requests: BoundedVec, + nullifier_key_validation_requests: BoundedVec, + + new_commitments: BoundedVec, + new_nullifiers: BoundedVec, + + private_call_stack_hashes : BoundedVec, + public_call_stack_hashes : BoundedVec, + new_l2_to_l1_msgs : BoundedVec, + // docs:end:private-context + + // Header of a block whose state is used during private execution (not the block the transaction is included in). + historical_header: Header, + + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + // encrypted_logs_preimages: Vec, + // unencrypted_logs_preimages: Vec, + + nullifier_key: Option, +} + +impl PrivateContext { + pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext { + PrivateContext { + inputs: inputs, + side_effect_counter: inputs.call_context.start_side_effect_counter, + meta_hwm: 0, + + args_hash: args_hash, + return_values: BoundedVec::new(0), + + read_requests: BoundedVec::new(SideEffect::empty()), + nullifier_key_validation_requests: BoundedVec::new(NullifierKeyValidationRequest::empty()), + + new_commitments: BoundedVec::new(SideEffect::empty()), + new_nullifiers: BoundedVec::new(SideEffectLinkedToNoteHash::empty()), + + historical_header: inputs.historical_header, + + private_call_stack_hashes: BoundedVec::new(0), + public_call_stack_hashes: BoundedVec::new(0), + new_l2_to_l1_msgs: BoundedVec::new(0), + + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + // encrypted_logs_preimages: Vec::new(), + // unencrypted_logs_preimages: Vec::new(), + + nullifier_key: Option::none(), + } + } + + pub fn msg_sender(self) -> AztecAddress { + self.inputs.call_context.msg_sender + } + + pub fn this_address(self) -> AztecAddress { + self.inputs.call_context.storage_contract_address + } + + pub fn this_portal_address(self) -> EthAddress { + self.inputs.call_context.portal_contract_address + } + + pub fn chain_id(self) -> Field { + self.inputs.private_global_variables.chain_id + } + + pub fn version(self) -> Field { + self.inputs.private_global_variables.version + } + + pub fn selector(self) -> FunctionSelector { + self.inputs.call_context.function_selector + } + + // Returns the header of a block whose state is used during private execution (not the block the transaction is + // included in). + pub fn get_header(self) -> Header { + self.historical_header + } + + // Returns the header of an arbitrary block whose block number is less than or equal to the block number + // of historical header. + pub fn get_header_at(self, block_number: u32) -> Header { + get_header_at(block_number, self) + } + + pub fn finish(self) -> PrivateCircuitPublicInputs { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + let encrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; + let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; + let encrypted_log_preimages_length = 0; + let unencrypted_log_preimages_length = 0; + + let priv_circuit_pub_inputs = PrivateCircuitPublicInputs { + call_context: self.inputs.call_context, + args_hash: self.args_hash, + return_values: self.return_values.storage, + meta_hwm: self.meta_hwm, + read_requests: self.read_requests.storage, + nullifier_key_validation_requests: self.nullifier_key_validation_requests.storage, + new_commitments: self.new_commitments.storage, + new_nullifiers: self.new_nullifiers.storage, + private_call_stack_hashes: self.private_call_stack_hashes.storage, + public_call_stack_hashes: self.public_call_stack_hashes.storage, + new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, + end_side_effect_counter: self.side_effect_counter, + encrypted_logs_hash: encrypted_logs_hash, + unencrypted_logs_hash: unencrypted_logs_hash, + encrypted_log_preimages_length: encrypted_log_preimages_length, + unencrypted_log_preimages_length: unencrypted_log_preimages_length, + historical_header: self.historical_header, + contract_deployment_data: self.inputs.contract_deployment_data, + chain_id: self.inputs.private_global_variables.chain_id, + version: self.inputs.private_global_variables.version, + }; + priv_circuit_pub_inputs + } + + pub fn push_read_request(&mut self, read_request: Field) { + let side_effect = SideEffect { + value: read_request, + counter: self.side_effect_counter, + }; + self.read_requests.push(side_effect); + self.side_effect_counter = self.side_effect_counter + 1; + } + + pub fn push_new_note_hash(&mut self, note_hash: Field) { + let side_effect = SideEffect { + value: note_hash, + counter: self.side_effect_counter, + }; + self.new_commitments.push(side_effect); + self.side_effect_counter = self.side_effect_counter + 1; + } + + pub fn push_new_nullifier(&mut self, nullifier: Field, nullified_commitment: Field) { + let side_effect = SideEffectLinkedToNoteHash { + value: nullifier, + note_hash: nullified_commitment, + counter: self.side_effect_counter, + }; + self.new_nullifiers.push(side_effect); + self.side_effect_counter = self.side_effect_counter + 1; + } + + pub fn request_nullifier_secret_key(&mut self, account: AztecAddress) -> GrumpkinPrivateKey { + let key_pair = if self.nullifier_key.is_none() { + let key_pair = get_nullifier_key_pair(account); + validate_nullifier_key_against_address(account, key_pair.public_key); + let request = NullifierKeyValidationRequest { public_key: key_pair.public_key, secret_key: key_pair.secret_key }; + self.nullifier_key_validation_requests.push(request); + self.nullifier_key = Option::some(key_pair); + key_pair + } else { + let key_pair = self.nullifier_key.unwrap_unchecked(); + // If MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL is larger than 1, need to update the way the key pair is cached. + assert(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL == 1); + assert(key_pair.account == account, "Cannot query nullifier key for more than one account per call"); + key_pair + }; + key_pair.secret_key + } + + // docs:start:context_message_portal + pub fn message_portal(&mut self, content: Field) + // docs:end:context_message_portal + { + self.new_l2_to_l1_msgs.push(content); + } + + // PrivateContextInputs must be temporarily passed in to prevent too many unknowns + // Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned + // docs:start:context_consume_l1_to_l2_message + // docs:start:consume_l1_to_l2_message + pub fn consume_l1_to_l2_message( + &mut self, + msg_key: Field, + content: Field, + secret: Field + ) + // docs:end:context_consume_l1_to_l2_message + { + let nullifier = process_l1_to_l2_message(self.historical_header.state.l1_to_l2_message_tree.root, self.this_address(), self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret); + + // Push nullifier (and the "commitment" corresponding to this can be "empty") + self.push_new_nullifier(nullifier, 0) + } + // docs:end:consume_l1_to_l2_message + + pub fn accumulate_encrypted_logs(&mut self, log: [Field; N]) { + let _void1 = self.inputs; + let _void2 = log; + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + } + + pub fn accumulate_unencrypted_logs(&mut self, log: T) { + let _void1 = self.inputs; + let _void2 = log; + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + } + + pub fn call_private_function( + &mut self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args: [Field; ARGS_COUNT] + ) -> [Field; RETURN_VALUES_LENGTH] { + let args_hash = hash_args(args); + assert(args_hash == arguments::pack_arguments(args)); + self.call_private_function_with_packed_args(contract_address, function_selector, args_hash) + } + + pub fn call_private_function_no_args( + &mut self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + ) -> [Field; RETURN_VALUES_LENGTH] { + self.call_private_function_with_packed_args(contract_address, function_selector, 0) + } + + pub fn call_private_function_with_packed_args( + &mut self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args_hash: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let item = call_private_function_internal( + contract_address, + function_selector, + args_hash, + self.side_effect_counter, + ); + + assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter); + self.side_effect_counter = item.public_inputs.end_side_effect_counter + 1; + + assert(contract_address.eq(item.contract_address)); + assert(function_selector.eq(item.function_data.selector)); + + assert(args_hash == item.public_inputs.args_hash); + + // Assert that the call context of the enqueued call generated by the oracle matches our request. + // We are issuing a regular call which is not delegate, static, or deployment. We also constrain + // the msg_sender in the nested call to be equal to our address, and the execution context address + // for the nested call to be equal to the address we actually called. + assert(item.public_inputs.call_context.is_delegate_call == false); + assert(item.public_inputs.call_context.is_static_call == false); + assert(item.public_inputs.call_context.is_contract_deployment == false); + assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)); + assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address)); + + self.private_call_stack_hashes.push(item.hash()); + + item.public_inputs.return_values + } + + pub fn call_public_function( + &mut self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args: [Field; ARGS_COUNT] + ) { + let args_hash = hash_args(args); + assert(args_hash == arguments::pack_arguments(args)); + self.call_public_function_with_packed_args(contract_address, function_selector, args_hash) + } + + pub fn call_public_function_no_args( + &mut self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + ) { + self.call_public_function_with_packed_args(contract_address, function_selector, 0) + } + + pub fn call_public_function_with_packed_args( + &mut self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args_hash: Field + ) { + let fields = enqueue_public_function_call_internal( + contract_address, + function_selector, + args_hash, + self.side_effect_counter + ); + + let mut reader = Reader::new(fields); + + // Note: Not using PublicCirclePublicInputs::deserialize here, because everything below args_hash is 0 and + // there is no more data in fields because there is only ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE fields! + let item = PublicCallStackItem { + contract_address: AztecAddress::from_field(reader.read()), + function_data: reader.read_struct(FunctionData::deserialize), + public_inputs: PublicCircuitPublicInputs { + call_context: reader.read_struct(CallContext::deserialize), + args_hash: reader.read(), + return_values: [0; RETURN_VALUES_LENGTH], + contract_storage_update_requests: [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL], + contract_storage_reads: [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL], + public_call_stack_hashes: [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], + new_commitments: [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL], + new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL], + new_l2_to_l1_msgs:[0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL], + unencrypted_logs_hash:[0; NUM_FIELDS_PER_SHA256], + unencrypted_log_preimages_length: 0, + historical_header: Header::empty(), + prover_address: AztecAddress::zero(), + }, + is_execution_request: true, + }; + reader.finish(); + + assert(contract_address.eq(item.contract_address)); + assert(function_selector.eq(item.function_data.selector)); + + assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter); + // We increment the sideffect counter by one, to account for the call itself being a side effect. + self.side_effect_counter = self.side_effect_counter + 1; + + assert(args_hash == item.public_inputs.args_hash); + + // Assert that the call context of the enqueued call generated by the oracle matches our request. + // We are issuing a regular call which is not delegate, static, or deployment. We also constrain + // the msg_sender in the nested call to be equal to our address, and the execution context address + // for the nested call to be equal to the address we actually called. + assert(item.public_inputs.call_context.is_delegate_call == false); + assert(item.public_inputs.call_context.is_static_call == false); + assert(item.public_inputs.call_context.is_contract_deployment == false); + assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)); + assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address)); + + self.public_call_stack_hashes.push(item.hash()); + } +} diff --git a/yarn-project/aztec-nr/aztec/src/context/public.nr b/yarn-project/aztec-nr/aztec/src/context/public.nr new file mode 100644 index 000000000000..87afe426fca0 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/context/public.nr @@ -0,0 +1,248 @@ +use crate::{ + abi::PublicContextInputs, + key::nullifier_key::validate_nullifier_key_against_address, + messaging::process_l1_to_l2_message, + oracle::{ + arguments, + public_call::call_public_function_internal, + context::get_portal_address, + header::get_header_at, + nullifier_key::{get_nullifier_key_pair, NullifierKeyPair}, + }, +}; +use dep::protocol_types::{ + abis::{ + append_only_tree_snapshot::AppendOnlyTreeSnapshot, + call_context::CallContext, + global_variables::GlobalVariables, + function_data::FunctionData, + function_selector::FunctionSelector, + nullifier_key_validation_request::NullifierKeyValidationRequest, + private_call_stack_item::PrivateCallStackItem, + private_circuit_public_inputs::PrivateCircuitPublicInputs, + public_call_stack_item::PublicCallStackItem, + public_circuit_public_inputs::PublicCircuitPublicInputs, + side_effect::{SideEffect, SideEffectLinkedToNoteHash}, + }, + address::{ + AztecAddress, + EthAddress, + }, + constants::{ + MAX_NEW_COMMITMENTS_PER_CALL, + MAX_NEW_L2_TO_L1_MSGS_PER_CALL, + MAX_NEW_NULLIFIERS_PER_CALL, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_DATA_READS_PER_CALL, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, + MAX_READ_REQUESTS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, + NUM_FIELDS_PER_SHA256, + RETURN_VALUES_LENGTH, + }, + contract_class::ContractClassId, + contrakt::{ + deployment_data::ContractDeploymentData, + storage_read::StorageRead, + storage_update_request::StorageUpdateRequest, + }, + grumpkin_point::GrumpkinPoint, + grumpkin_private_key::GrumpkinPrivateKey, + hash::hash_args, + header::Header, + state_reference::StateReference, + utils::reader::Reader, +}; +use dep::std::{ + option::Option, +}; + +struct PublicContext { + inputs: PublicContextInputs, + side_effect_counter: u32, + + args_hash : Field, + return_values : BoundedVec, + + contract_storage_update_requests: BoundedVec, + contract_storage_reads: BoundedVec, + public_call_stack_hashes: BoundedVec, + + new_commitments: BoundedVec, + new_nullifiers: BoundedVec, + + new_l2_to_l1_msgs: BoundedVec, + + unencrypted_logs_hash: BoundedVec, + unencrypted_logs_preimages_length: Field, + + // Header of a block whose state is used during public execution. Set by sequencer to be a header of a block + // previous to the one in which the tx is included. + historical_header: Header, + prover_address: AztecAddress, +} + +impl PublicContext { + pub fn new(inputs: PublicContextInputs, args_hash: Field) -> PublicContext { + let empty_storage_read = StorageRead::empty(); + let empty_storage_update = StorageUpdateRequest::empty(); + PublicContext { + inputs: inputs, + side_effect_counter: inputs.call_context.start_side_effect_counter, + + args_hash: args_hash, + return_values: BoundedVec::new(0), + + contract_storage_update_requests: BoundedVec::new(empty_storage_update), + contract_storage_reads: BoundedVec::new(empty_storage_read), + public_call_stack_hashes: BoundedVec::new(0), + + new_commitments: BoundedVec::new(SideEffect::empty()), + new_nullifiers: BoundedVec::new(SideEffectLinkedToNoteHash::empty()), + + new_l2_to_l1_msgs: BoundedVec::new(0), + + + unencrypted_logs_hash: BoundedVec::new(0), + unencrypted_logs_preimages_length: 0, + + historical_header: inputs.historical_header, + prover_address: AztecAddress::zero(), + + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + // encrypted_logs_preimages: Vec::new(), + // unencrypted_logs_preimages: Vec::new(), + } + } + + pub fn msg_sender(self) -> AztecAddress { + self.inputs.call_context.msg_sender + } + + pub fn this_address(self) -> AztecAddress { + self.inputs.call_context.storage_contract_address + } + + pub fn this_portal_address(self) -> EthAddress { + self.inputs.call_context.portal_contract_address + } + + pub fn chain_id(self) -> Field { + self.inputs.public_global_variables.chain_id + } + + pub fn version(self) -> Field { + self.inputs.public_global_variables.version + } + + pub fn selector(self) -> FunctionSelector { + self.inputs.call_context.function_selector + } + + pub fn block_number(self) -> Field { + self.inputs.public_global_variables.block_number + } + + pub fn timestamp(self) -> Field { + self.inputs.public_global_variables.timestamp + } + + pub fn finish(self) -> PublicCircuitPublicInputs { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; + let unencrypted_log_preimages_length = 0; + + + // Compute the public call stack hashes + let pub_circuit_pub_inputs = PublicCircuitPublicInputs { + call_context: self.inputs.call_context, // Done + args_hash: self.args_hash, // Done + contract_storage_update_requests: self.contract_storage_update_requests.storage, + contract_storage_reads: self.contract_storage_reads.storage, + return_values: self.return_values.storage, + new_commitments: self.new_commitments.storage, + new_nullifiers: self.new_nullifiers.storage, + public_call_stack_hashes: self.public_call_stack_hashes.storage, + new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, + unencrypted_logs_hash: unencrypted_logs_hash, + unencrypted_log_preimages_length: unencrypted_log_preimages_length, + historical_header: self.inputs.historical_header, + prover_address: self.prover_address, + }; + pub_circuit_pub_inputs + } + + pub fn push_new_note_hash(&mut self, note_hash: Field) { + let side_effect = SideEffect { + value: note_hash, + counter: self.side_effect_counter + }; + self.new_commitments.push(side_effect); + self.side_effect_counter = self.side_effect_counter + 1; + } + + pub fn push_new_nullifier(&mut self, nullifier: Field, _nullified_commitment: Field) { + let side_effect = SideEffectLinkedToNoteHash { + value: nullifier, + note_hash: 0, // cannot nullify pending notes in public context + counter: self.side_effect_counter + }; + self.new_nullifiers.push(side_effect); + self.side_effect_counter = self.side_effect_counter + 1; + } + + pub fn message_portal(&mut self, content: Field) { + self.new_l2_to_l1_msgs.push(content); + } + + // PrivateContextInputs must be temporarily passed in to prevent too many unknowns + // Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned + pub fn consume_l1_to_l2_message(&mut self, msg_key: Field, content: Field, secret: Field) { + let this = (*self).this_address(); + let nullifier = process_l1_to_l2_message(self.historical_header.state.l1_to_l2_message_tree.root, this, self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret); + + // Push nullifier (and the "commitment" corresponding to this can be "empty") + self.push_new_nullifier(nullifier, 0) + } + + pub fn accumulate_encrypted_logs(&mut self, log: [Field; N]) { + let _void1 = self; + let _void2 = log; + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + } + + pub fn accumulate_unencrypted_logs(&mut self, log: T) { + let _void1 = self; + let _void2 = log; + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + } + + pub fn call_public_function( + _self: Self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args: [Field; ARGS_COUNT], + ) -> [Field; RETURN_VALUES_LENGTH] { + let args_hash = hash_args(args); + assert(args_hash == arguments::pack_arguments(args)); + call_public_function_internal( + contract_address, + function_selector, + args_hash, + ) + } + + pub fn call_public_function_no_args( + _self: Self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + ) -> [Field; RETURN_VALUES_LENGTH] { + call_public_function_internal( + contract_address, + function_selector, + 0, + ) + } + +} diff --git a/yarn-project/aztec-nr/aztec/src/context/public_vm.nr b/yarn-project/aztec-nr/aztec/src/context/public_vm.nr new file mode 100644 index 000000000000..455a6547382c --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/context/public_vm.nr @@ -0,0 +1,54 @@ +use dep::protocol_types::address::{ + AztecAddress, + EthAddress, +}; + +// Getters that will be converted by the transpiler into their +// own opcodes +struct PublicVmContext {} + +impl PublicVmContext { + // Empty new function enables retaining context. syntax + pub fn new() -> Self { + PublicVmContext {} + } + + #[oracle(address)] + pub fn address(self) -> AztecAddress {} + + #[oracle(storageAddress)] + pub fn storage_address(self) -> AztecAddress {} + + #[oracle(origin)] + pub fn origin(self) -> AztecAddress {} + + #[oracle(sender)] + pub fn sender(self) -> AztecAddress {} + + #[oracle(portal)] + pub fn portal(self) -> EthAddress {} + + #[oracle(feePerL1Gas)] + pub fn fee_per_l1_gas(self) -> Field {} + + #[oracle(feePerL2Gas)] + pub fn fee_per_l2_gas(self) -> Field {} + + #[oracle(feePerDaGas)] + pub fn fee_per_da_gas(self) -> Field {} + + #[oracle(chainId)] + pub fn chain_id(self) -> Field {} + + #[oracle(version)] + pub fn version(self) -> Field {} + + #[oracle(blockNumber)] + pub fn block_number(self) -> Field {} + + #[oracle(timestamp)] + pub fn timestamp(self) -> Field {} + + // #[oracle(contractCallDepth)] + // pub fn contract_call_depth(self) -> Field {} +} diff --git a/yarn-project/aztec-nr/aztec/src/lib.nr b/yarn-project/aztec-nr/aztec/src/lib.nr index 88866fb2fbcf..7a8531601c4f 100644 --- a/yarn-project/aztec-nr/aztec/src/lib.nr +++ b/yarn-project/aztec-nr/aztec/src/lib.nr @@ -1,5 +1,4 @@ mod abi; -mod avm; mod context; mod hash; mod history; diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr index 107b94940465..5aca7a22b388 100644 --- a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -7,9 +7,6 @@ contract AvmTest { EthAddress, }; - // avm lib - use dep::aztec::avm::context::AvmContext; - #[aztec(private)] fn constructor() {} @@ -24,67 +21,67 @@ contract AvmTest { ************************************************************************/ #[aztec(public-vm)] fn getAddress() -> pub AztecAddress { - AvmContext::address() + context.address() } #[aztec(public-vm)] fn getStorageAddress() -> pub AztecAddress { - AvmContext::storage_address() + context.storage_address() } #[aztec(public-vm)] fn getSender() -> pub AztecAddress { - AvmContext::sender() + context.sender() } #[aztec(public-vm)] fn getOrigin() -> pub AztecAddress { - AvmContext::origin() + context.origin() } #[aztec(public-vm)] fn getPortal() -> pub EthAddress { - AvmContext::portal() + context.portal() } #[aztec(public-vm)] fn getFeePerL1Gas() -> pub Field { - AvmContext::fee_per_l1_gas() + context.fee_per_l1_gas() } #[aztec(public-vm)] fn getFeePerL2Gas() -> pub Field { - AvmContext::fee_per_l2_gas() + context.fee_per_l2_gas() } #[aztec(public-vm)] fn getFeePerDaGas() -> pub Field { - AvmContext::fee_per_da_gas() + context.fee_per_da_gas() } #[aztec(public-vm)] fn getChainId() -> pub Field { - AvmContext::chain_id() + context.chain_id() } #[aztec(public-vm)] fn getVersion() -> pub Field { - AvmContext::version() + context.version() } #[aztec(public-vm)] fn getBlockNumber() -> pub Field { - AvmContext::block_number() + context.block_number() } #[aztec(public-vm)] fn getTimestamp() -> pub Field { - AvmContext::timestamp() + context.timestamp() } // #[aztec(public-vm)] // fn getContractCallDepth() -> pub Field { - // AvmContext::contract_call_depth() + // context.contract_call_depth() // } // Function required for all contracts From 7578f152c369110cae154426cb2304517e107d94 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Mon, 5 Feb 2024 23:11:59 +0000 Subject: [PATCH 6/7] fmt --- noir/aztec_macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index 1f55478338da..df4b0a14891c 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -1167,7 +1167,7 @@ fn create_avm_context() -> Result { "context", // Assigned to call( variable_path(chained_path!("aztec", "context", "PublicVmContext", "new")), // Path - vec![], // args + vec![], // args ), ); From 44a4b908664d43603beb772da369ed931cbd92b8 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Mon, 5 Feb 2024 23:25:59 +0000 Subject: [PATCH 7/7] cleanup --- noir/aztec_macros/src/lib.rs | 21 +++++++++++++++++-- yarn-project/aztec-nr/aztec/src/context.nr | 4 ++-- .../src/context/{public_vm.nr => avm.nr} | 6 +++--- .../aztec-nr/aztec/src/context/private.nr | 14 ++----------- .../aztec-nr/aztec/src/context/public.nr | 19 ----------------- 5 files changed, 26 insertions(+), 38 deletions(-) rename yarn-project/aztec-nr/aztec/src/context/{public_vm.nr => avm.nr} (93%) diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index df4b0a14891c..138bcafb1d61 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -1162,12 +1162,29 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac Ok(injected_expressions) } +/// Creates an mutable avm context +/// +/// ```noir +/// /// Before +/// #[aztec(public-vm)] +/// fn foo() -> Field { +/// let mut context = aztec::context::AVMContext::new(); +/// let timestamp = context.timestamp(); +/// // ... +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() -> Field { +/// let mut timestamp = context.timestamp(); +/// // ... +/// } fn create_avm_context() -> Result { let let_context = mutable_assignment( "context", // Assigned to call( - variable_path(chained_path!("aztec", "context", "PublicVmContext", "new")), // Path - vec![], // args + variable_path(chained_path!("aztec", "context", "AVMContext", "new")), // Path + vec![], // args ), ); diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index 77fda459f2b0..911ee98f17c2 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -1,10 +1,10 @@ mod private; mod public; -mod public_vm; +mod avm; use public::PublicContext; use private::PrivateContext; -use public_vm::PublicVmContext; +use avm::AVMContext; struct Context { private: Option<&mut PrivateContext>, diff --git a/yarn-project/aztec-nr/aztec/src/context/public_vm.nr b/yarn-project/aztec-nr/aztec/src/context/avm.nr similarity index 93% rename from yarn-project/aztec-nr/aztec/src/context/public_vm.nr rename to yarn-project/aztec-nr/aztec/src/context/avm.nr index 455a6547382c..98c578e59db3 100644 --- a/yarn-project/aztec-nr/aztec/src/context/public_vm.nr +++ b/yarn-project/aztec-nr/aztec/src/context/avm.nr @@ -5,12 +5,12 @@ use dep::protocol_types::address::{ // Getters that will be converted by the transpiler into their // own opcodes -struct PublicVmContext {} +struct AVMContext {} -impl PublicVmContext { +impl AVMContext { // Empty new function enables retaining context. syntax pub fn new() -> Self { - PublicVmContext {} + Self {} } #[oracle(address)] diff --git a/yarn-project/aztec-nr/aztec/src/context/private.nr b/yarn-project/aztec-nr/aztec/src/context/private.nr index 525c05e17e47..1692808fbdbf 100644 --- a/yarn-project/aztec-nr/aztec/src/context/private.nr +++ b/yarn-project/aztec-nr/aztec/src/context/private.nr @@ -1,14 +1,10 @@ use crate::{ - abi::{ - PrivateContextInputs, - PublicContextInputs, - }, + abi::PrivateContextInputs, key::nullifier_key::validate_nullifier_key_against_address, messaging::process_l1_to_l2_message, oracle::{ arguments, call_private_function::call_private_function_internal, - public_call::call_public_function_internal, enqueue_public_function_call::enqueue_public_function_call_internal, context::get_portal_address, header::get_header_at, @@ -17,9 +13,7 @@ use crate::{ }; use dep::protocol_types::{ abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, call_context::CallContext, - global_variables::GlobalVariables, function_data::FunctionData, function_selector::FunctionSelector, nullifier_key_validation_request::NullifierKeyValidationRequest, @@ -52,16 +46,12 @@ use dep::protocol_types::{ storage_read::StorageRead, storage_update_request::StorageUpdateRequest, }, - grumpkin_point::GrumpkinPoint, grumpkin_private_key::GrumpkinPrivateKey, hash::hash_args, header::Header, - state_reference::StateReference, utils::reader::Reader, }; -use dep::std::{ - option::Option, -}; +use dep::std::option::Option; // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) // use dep::std::collections::vec::Vec; diff --git a/yarn-project/aztec-nr/aztec/src/context/public.nr b/yarn-project/aztec-nr/aztec/src/context/public.nr index 87afe426fca0..d7577654148b 100644 --- a/yarn-project/aztec-nr/aztec/src/context/public.nr +++ b/yarn-project/aztec-nr/aztec/src/context/public.nr @@ -1,24 +1,15 @@ use crate::{ abi::PublicContextInputs, - key::nullifier_key::validate_nullifier_key_against_address, messaging::process_l1_to_l2_message, oracle::{ arguments, public_call::call_public_function_internal, - context::get_portal_address, - header::get_header_at, - nullifier_key::{get_nullifier_key_pair, NullifierKeyPair}, }, }; use dep::protocol_types::{ abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, - call_context::CallContext, global_variables::GlobalVariables, - function_data::FunctionData, function_selector::FunctionSelector, - nullifier_key_validation_request::NullifierKeyValidationRequest, - private_call_stack_item::PrivateCallStackItem, private_circuit_public_inputs::PrivateCircuitPublicInputs, public_call_stack_item::PublicCallStackItem, public_circuit_public_inputs::PublicCircuitPublicInputs, @@ -32,31 +23,21 @@ use dep::protocol_types::{ MAX_NEW_COMMITMENTS_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, - MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, MAX_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH, }, - contract_class::ContractClassId, contrakt::{ - deployment_data::ContractDeploymentData, storage_read::StorageRead, storage_update_request::StorageUpdateRequest, }, - grumpkin_point::GrumpkinPoint, - grumpkin_private_key::GrumpkinPrivateKey, hash::hash_args, header::Header, - state_reference::StateReference, utils::reader::Reader, }; -use dep::std::{ - option::Option, -}; struct PublicContext { inputs: PublicContextInputs,