From 425f059c14ac1d41184943b05d692f3cf08e01d2 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Tue, 2 Apr 2024 16:39:09 +0000 Subject: [PATCH] feat(avm): make authwit work with avm --- noir-projects/aztec-nr/authwit/src/auth.nr | 16 +++-- .../avm_initializer_test_contract/src/main.nr | 3 +- .../contracts/avm_test_contract/Nargo.toml | 3 +- .../contracts/avm_test_contract/src/main.nr | 9 +++ yarn-project/end-to-end/package.json | 1 + .../end-to-end/src/e2e_avm_simulator.test.ts | 63 ++++++++++++++----- .../src/avm/opcodes/external_calls.ts | 3 + .../src/public/public_execution_context.ts | 1 + 8 files changed, 79 insertions(+), 20 deletions(-) diff --git a/noir-projects/aztec-nr/authwit/src/auth.nr b/noir-projects/aztec-nr/authwit/src/auth.nr index c1c126be9a2e..4442575f22ed 100644 --- a/noir-projects/aztec-nr/authwit/src/auth.nr +++ b/noir-projects/aztec-nr/authwit/src/auth.nr @@ -2,7 +2,10 @@ use dep::aztec::protocol_types::{ abis::function_selector::FunctionSelector, address::AztecAddress, constants::{GENERATOR_INDEX__AUTHWIT_INNER, GENERATOR_INDEX__AUTHWIT_OUTER}, hash::pedersen_hash }; -use dep::aztec::{context::{PrivateContext, PublicContext, Context}, hash::hash_args_array}; +use dep::aztec::{ + context::{PrivateContext, PublicContext, Context, interface::{ContextInterface, PublicContextInterface}}, + hash::hash_args_array +}; global IS_VALID_SELECTOR = 0xabf64ad4; // 4 first bytes of keccak256("IS_VALID()") @@ -18,10 +21,15 @@ pub fn assert_current_call_valid_authwit(context: &mut PrivateContext, on_behalf // docs:start:assert_current_call_valid_authwit_public // Assert that `on_behalf_of` have authorized the current call in a public context -pub fn assert_current_call_valid_authwit_public(context: &mut PublicContext, on_behalf_of: AztecAddress) { +pub fn assert_current_call_valid_authwit_public( + context: &mut TPublicContext, + on_behalf_of: AztecAddress +) where TPublicContext: ContextInterface + PublicContextInterface { let function_selector = FunctionSelector::from_signature("spend_public_authwit(Field)"); - let inner_hash = compute_inner_authwit_hash([context.msg_sender().to_field(), context.selector().to_field(), context.args_hash]); - let result = context.call_public_function(on_behalf_of, function_selector, [inner_hash])[0]; + let inner_hash = compute_inner_authwit_hash( + [(*context).msg_sender().to_field(), (*context).selector().to_field(), (*context).get_args_hash()] + ); + let result = PublicContextInterface::call_public_function(context, on_behalf_of, function_selector, [inner_hash])[0]; assert(result == IS_VALID_SELECTOR, "Message not authorized by account"); } // docs:end:assert_current_call_valid_authwit_public diff --git a/noir-projects/noir-contracts/contracts/avm_initializer_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_initializer_test_contract/src/main.nr index d2557a38a8d3..a71cf2a1de44 100644 --- a/noir-projects/noir-contracts/contracts/avm_initializer_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_initializer_test_contract/src/main.nr @@ -17,7 +17,8 @@ contract AvmInitializerTest { storage.immutable.initialize(42); } - unconstrained fn view_storage_immutable() -> pub Field { + #[aztec(public-vm)] + fn read_storage_immutable() -> pub Field { storage.immutable.read() } } diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/avm_test_contract/Nargo.toml index dd0d58eea3b6..dff79424f9e5 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/Nargo.toml @@ -6,4 +6,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } -compressed_string = { path = "../../../aztec-nr/compressed-string" } \ No newline at end of file +compressed_string = { path = "../../../aztec-nr/compressed-string" } +authwit = { path = "../../../aztec-nr/authwit" } diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 474e6f05a723..6aeac699cdf3 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -37,6 +37,7 @@ contract AvmTest { use dep::aztec::protocol_types::traits::ToField; use dep::aztec::protocol_types::constants::RETURN_VALUES_LENGTH; use dep::compressed_string::CompressedString; + use dep::authwit::{auth::{assert_current_call_valid_authwit, assert_current_call_valid_authwit_public}}; // avm lib use dep::aztec::avm::hash::{keccak256, poseidon, sha256}; @@ -252,6 +253,14 @@ contract AvmTest { assert(ci.is_some()); } + /************************************************************************ + * Authwit functions + ************************************************************************/ + #[aztec(public-vm)] + fn test_authwit_send_money(from: AztecAddress, _to: AztecAddress, _amount: Field) { + assert_current_call_valid_authwit_public(&mut context, from); + } + /************************************************************************ * AvmContext functions ************************************************************************/ diff --git a/yarn-project/end-to-end/package.json b/yarn-project/end-to-end/package.json index 091c8ca0c1d2..9e05ab114463 100644 --- a/yarn-project/end-to-end/package.json +++ b/yarn-project/end-to-end/package.json @@ -15,6 +15,7 @@ "clean": "rm -rf ./dest .tsbuildinfo", "formatting": "run -T prettier --check ./src \"!src/web/main.js\" && run -T eslint ./src", "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src", + "test:lean": "DEBUG='aztec:*,-*avm_simulator:memory,-*avm_simulator:core*' NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --runInBand --testTimeout=60000 --forceExit", "test": "DEBUG='aztec:*' NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --runInBand --testTimeout=60000 --forceExit", "test:integration": "concurrently -k -s first -c reset,dim -n test,anvil \"yarn test:integration:run\" \"anvil\"", "test:integration:run": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --no-cache --runInBand --config jest.integration.config.json" diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index a40731b019ac..f7c7dadef8da 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -1,20 +1,21 @@ -import { AztecAddress, Fr, FunctionSelector, TxStatus, type Wallet } from '@aztec/aztec.js'; +import { type AccountWallet, AztecAddress, Fr, FunctionSelector, TxStatus } from '@aztec/aztec.js'; import { AvmInitializerTestContract, AvmTestContract } from '@aztec/noir-contracts.js'; import { jest } from '@jest/globals'; -import { setup } from './fixtures/utils.js'; +import { publicDeployAccounts, setup } from './fixtures/utils.js'; const TIMEOUT = 100_000; describe('e2e_avm_simulator', () => { jest.setTimeout(TIMEOUT); - let wallet: Wallet; + let wallet: AccountWallet; let teardown: () => Promise; beforeAll(async () => { ({ teardown, wallet } = await setup()); + await publicDeployAccounts(wallet, [wallet]); }, 100_000); afterAll(() => teardown()); @@ -78,7 +79,8 @@ describe('e2e_avm_simulator', () => { expect(await avmContract.methods.call_acvm_from_avm().simulate()).toEqual([123456n, 0n, 0n, 0n]); }); - // Cannot work because ACVM does not support pending nullifiers. + // This will not work because the ACVM does not see pending nullifiers. + // // Cannot work because ACVM does not support pending nullifiers. // it('AVM->ACVM nullifiers work (pending)', async () => { // await avmContract.methods.avm_to_acvm_nullifier().send().wait(); // }); @@ -96,20 +98,53 @@ describe('e2e_avm_simulator', () => { .avm_to_acvm_call(FunctionSelector.fromSignature('assert_unsiloed_nullifier_acvm(Field)'), nullifier) .send() .wait(); + // }); }); - }); - }); - describe('AvmInitializerTestContract', () => { - let avmContract: AvmInitializerTestContract; + describe('Authwit', () => { + it('Works if authwit provided', async () => { + const recipient = AztecAddress.random(); + const action = avmContract.methods.test_authwit_send_money( + /*from=*/ wallet.getCompleteAddress(), + recipient, + 100, + ); + let tx = await wallet + .setPublicAuthWit({ caller: wallet.getCompleteAddress().address, action }, /*authorized=*/ true) + .send() + .wait(); + expect(tx.status).toEqual(TxStatus.MINED); + + tx = await avmContract.methods + .test_authwit_send_money(/*from=*/ wallet.getCompleteAddress(), recipient, 100) + .send() + .wait(); + expect(tx.status).toEqual(TxStatus.MINED); + }); + + it('Fails if authwit not provided', async () => { + await expect( + async () => + await avmContract.methods + .test_authwit_send_money(/*from=*/ wallet.getCompleteAddress(), /*to=*/ AztecAddress.random(), 100) + .send() + .wait(), + ).rejects.toThrow(/Message not authorized by account/); + }); + }); - beforeEach(async () => { - avmContract = await AvmInitializerTestContract.deploy(wallet).send().deployed(); - }, 50_000); + describe('AvmInitializerTestContract', () => { + let avmContract: AvmInitializerTestContract; - describe('Storage', () => { - it('Read immutable (initialized) storage (Field)', async () => { - expect(await avmContract.methods.view_storage_immutable().simulate()).toEqual(42n); + beforeEach(async () => { + avmContract = await AvmInitializerTestContract.deploy(wallet).send().deployed(); + }, 50_000); + + describe('Storage', () => { + it('Read immutable (initialized) storage (Field)', async () => { + expect(await avmContract.methods.read_storage_immutable().simulate()).toEqual([42n, 0n, 0n, 0n]); + }); + }); }); }); }); diff --git a/yarn-project/simulator/src/avm/opcodes/external_calls.ts b/yarn-project/simulator/src/avm/opcodes/external_calls.ts index 76b05dc51ccd..76650e41cde6 100644 --- a/yarn-project/simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/simulator/src/avm/opcodes/external_calls.ts @@ -78,11 +78,14 @@ abstract class ExternalCall extends Instruction { const pxContext = createPublicExecutionContext(nestedContext, calldata); const pxResults = await executePublicFunction(pxContext, /*nested=*/ true); const nestedCallResults: AvmContractCallResults = convertPublicExecutionResult(pxResults); + adjustAvmContextFromPublicExecutionResult(nestedContext, pxResults); const nestedPersistableState = nestedContext.persistableState; // const nestedContext = context.createNestedContractCallContext( // callAddress.toFr(), // calldata, + // allocatedGas, + // this.type, // FunctionSelector.fromField(functionSelector), // ); // const nestedCallResults: AvmContractCallResults = await new AvmSimulator(nestedContext).execute(); diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index 82d2ac393ede..59782ba306c1 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -245,6 +245,7 @@ export class PublicExecutionContext extends TypedOracle { if (!this.header.globalVariables.blockNumber.equals(new Fr(blockNumber))) { throw new Error(`Public execution oracle can only access nullifier membership witnesses for the current block`); } + this.log(`Getting nullifier membership witness for nullifier=${nullifier.toString()}`); return await this.commitmentsDb.getNullifierMembershipWitnessAtLatestBlock(nullifier); }