From bf6df0068e8cf212d645eeae41d852e8437a3b47 Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 17 Jan 2024 11:48:49 +0000 Subject: [PATCH 01/16] initial static impl private --- .../acir-simulator/src/acvm/oracle/oracle.ts | 4 ++- .../src/acvm/oracle/typed_oracle.ts | 1 + .../src/client/client_execution_context.ts | 8 ++++- .../aztec-nr/authwit/src/entrypoint.nr | 2 +- yarn-project/aztec-nr/aztec/src/context.nr | 29 ++++++++++++++++--- .../aztec/src/oracle/call_private_function.nr | 9 ++++-- .../src/e2e_nested_contract.test.ts | 8 +++++ .../contracts/parent_contract/src/main.nr | 13 +++++++++ .../pxe/src/pxe_service/pxe_service.ts | 9 +++++- 9 files changed, 72 insertions(+), 11 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 49449ffcbcb..f6d2495590c 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -7,7 +7,7 @@ import { Fr, Point } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { ACVMField } from '../acvm_types.js'; -import { frToNumber, fromACVMField } from '../deserialize.js'; +import { frToBoolean, frToNumber, fromACVMField } from '../deserialize.js'; import { toACVMField, toAcvmCallPrivateStackItem, @@ -292,12 +292,14 @@ export class Oracle { [functionSelector]: ACVMField[], [argsHash]: ACVMField[], [sideffectCounter]: ACVMField[], + [isStaticCall]: ACVMField[], ): Promise { const callStackItem = await this.typedOracle.callPrivateFunction( AztecAddress.fromField(fromACVMField(contractAddress)), FunctionSelector.fromField(fromACVMField(functionSelector)), fromACVMField(argsHash), frToNumber(fromACVMField(sideffectCounter)), + frToBoolean(fromACVMField(isStaticCall)), ); return toAcvmCallPrivateStackItem(callStackItem); } diff --git a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts index 995a285b7b1..09e4a40f28b 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -182,6 +182,7 @@ export abstract class TypedOracle { _functionSelector: FunctionSelector, _argsHash: Fr, _sideffectCounter: number, + _isStaticCall: boolean, ): Promise { throw new Error('Not available.'); } diff --git a/yarn-project/acir-simulator/src/client/client_execution_context.ts b/yarn-project/acir-simulator/src/client/client_execution_context.ts index 062cfdf4def..b9d59d4a4cd 100644 --- a/yarn-project/acir-simulator/src/client/client_execution_context.ts +++ b/yarn-project/acir-simulator/src/client/client_execution_context.ts @@ -300,6 +300,7 @@ export class ClientExecutionContext extends ViewDataOracle { * @param functionSelector - The function selector of the function to call. * @param argsHash - The packed arguments to pass to the function. * @param sideffectCounter - The side effect counter at the start of the call. + * @param isStaticCall - Whether the call is a static call. * @returns The execution result. */ async callPrivateFunction( @@ -307,6 +308,7 @@ export class ClientExecutionContext extends ViewDataOracle { functionSelector: FunctionSelector, argsHash: Fr, sideffectCounter: number, + isStaticCall: boolean, ) { this.log( `Calling private function ${this.contractAddress}:${functionSelector} from ${this.callContext.storageContractAddress}`, @@ -329,7 +331,7 @@ export class ClientExecutionContext extends ViewDataOracle { targetArtifact, sideffectCounter, false, - false, + isStaticCall, ); const context = new ClientExecutionContext( @@ -352,6 +354,10 @@ export class ClientExecutionContext extends ViewDataOracle { targetFunctionData, ); + if (childExecutionResult.newNotes.length > 0 && isStaticCall) { + throw new Error('Static call cannot create new notes'); + } + this.nestedExecutions.push(childExecutionResult); return childExecutionResult.callStackItem; diff --git a/yarn-project/aztec-nr/authwit/src/entrypoint.nr b/yarn-project/aztec-nr/authwit/src/entrypoint.nr index 82c3f5f6e2f..eae75a7ef35 100644 --- a/yarn-project/aztec-nr/authwit/src/entrypoint.nr +++ b/yarn-project/aztec-nr/authwit/src/entrypoint.nr @@ -107,7 +107,7 @@ impl EntrypointPayload { ); } else { let _result = context.call_private_function_with_packed_args( - call.target_address, call.function_selector, call.args_hash + call.target_address, call.function_selector, call.args_hash, false ); } } diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index b89e570f53f..b1edb53bb85 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -247,7 +247,18 @@ impl PrivateContext { ) -> [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) + self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, false) + } + + pub fn call_private_function_static( + &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, true) } pub fn call_private_function_no_args( @@ -255,20 +266,30 @@ impl PrivateContext { contract_address: AztecAddress, function_selector: FunctionSelector, ) -> [Field; RETURN_VALUES_LENGTH] { - self.call_private_function_with_packed_args(contract_address, function_selector, 0) + self.call_private_function_with_packed_args(contract_address, function_selector, 0, false) + } + + pub fn call_private_function_no_args_static( + &mut self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + ) -> [Field; RETURN_VALUES_LENGTH] { + self.call_private_function_with_packed_args(contract_address, function_selector, 0, true) } pub fn call_private_function_with_packed_args( &mut self, contract_address: AztecAddress, function_selector: FunctionSelector, - args_hash: Field + args_hash: Field, + is_static_call: bool ) -> [Field; RETURN_VALUES_LENGTH] { let fields = call_private_function_internal( contract_address, function_selector, args_hash, self.side_effect_counter, + is_static_call, ); let mut reader = Reader::new(fields); @@ -347,7 +368,7 @@ impl PrivateContext { // 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_static_call == is_static_call); 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)); diff --git a/yarn-project/aztec-nr/aztec/src/oracle/call_private_function.nr b/yarn-project/aztec-nr/aztec/src/oracle/call_private_function.nr index 98a0ab385a2..d39a48f60f2 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/call_private_function.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/call_private_function.nr @@ -9,19 +9,22 @@ fn call_private_function_oracle( _contract_address: AztecAddress, _function_selector: FunctionSelector, _args_hash: Field, - _start_side_effect_counter: u32 + _start_side_effect_counter: u32, + _is_static_call: bool ) -> [Field; CALL_PRIVATE_FUNCTION_RETURN_SIZE] {} unconstrained pub fn call_private_function_internal( contract_address: AztecAddress, function_selector: FunctionSelector, args_hash: Field, - start_side_effect_counter: u32 + start_side_effect_counter: u32, + is_static_call: bool ) -> [Field; CALL_PRIVATE_FUNCTION_RETURN_SIZE] { call_private_function_oracle( contract_address, function_selector, args_hash, - start_side_effect_counter + start_side_effect_counter, + is_static_call ) } diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index f1f4d29b3ae..970f84338bd 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -130,6 +130,14 @@ describe('e2e_nested_contract', () => { expect(processedLogs).toEqual([20n, 40n]); expect(await getChildStoredValue(childContract)).toEqual(new Fr(40n)); }); + + // eslint-disable-next-line no-only-tests/no-only-tests + it.only('performs static calls', async () => { + await parentContract.methods + .privateStaticCall(childContract.address, childContract.methods.value.selector) + .send() + .wait(); + }, 100_000); }); describe('importer uses autogenerated test contract interface', () => { diff --git a/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr b/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr index 7992b1b83e8..a19cea0d568 100644 --- a/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr @@ -138,4 +138,17 @@ contract Parent { [targetContract.to_field(), targetSelector.to_field(), targetValue + 1] ); } + + // Private function to call another private function in the targetContract using the provided selector + #[aztec(private)] + fn privateStaticCall( + targetContract: AztecAddress, + targetSelector: FunctionSelector + ) -> Field { + // Call the target private function + let return_values = context.call_private_function_static(targetContract, targetSelector, [0]); + + // Copy the return value from the call to this function's return values + return_values[0] + } } diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 5ae775e29b9..f8180097e89 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -46,7 +46,7 @@ import { PublicCallRequest, } from '@aztec/circuits.js'; import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/abis'; -import { DecodedReturn, encodeArguments } from '@aztec/foundation/abi'; +import { DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { SerialQueue } from '@aztec/foundation/fifo'; @@ -208,6 +208,13 @@ export class PXEService implements PXE { } public async addContracts(contracts: DeployedContract[]) { + contracts.forEach(c => + console.log( + `Adding contract ${c.artifact.name} and selectors ${c.artifact.functions.map(f => + FunctionSelector.fromNameAndParameters(f.name, f.parameters), + )}`, + ), + ); const contractDaos = contracts.map(c => new ContractDao(c.artifact, c.completeAddress, c.portalContract)); await Promise.all(contractDaos.map(c => this.db.addContract(c))); for (const contract of contractDaos) { From 91e60ce23a20bb6a44c838e870af6753d87f1fcc Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 17 Jan 2024 15:14:17 +0000 Subject: [PATCH 02/16] initial version for private calls --- .../src/e2e_nested_contract.test.ts | 17 +++++++++++--- .../contracts/child_contract/Nargo.toml | 1 + .../contracts/child_contract/src/main.nr | 22 ++++++++++++++++++- .../contracts/parent_contract/src/main.nr | 17 ++++++++++++-- .../pxe/src/pxe_service/pxe_service.ts | 9 +------- 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index 970f84338bd..ef2d93e8c2b 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -131,13 +131,24 @@ describe('e2e_nested_contract', () => { expect(await getChildStoredValue(childContract)).toEqual(new Fr(40n)); }); - // eslint-disable-next-line no-only-tests/no-only-tests - it.only('performs static calls', async () => { + it('performs legal private static calls', async () => { await parentContract.methods - .privateStaticCall(childContract.address, childContract.methods.value.selector) + .privateNoArgsStaticCall(childContract.address, childContract.methods.value.selector) .send() .wait(); }, 100_000); + + it('fails when performing illegal static calls', async () => { + await expect( + parentContract.methods + .privateStaticCall(childContract.address, childContract.methods.privateSetValue.selector, [ + 42n, + wallet.getCompleteAddress().address, + ]) + .send() + .wait(), + ).rejects.toThrowError('Static call cannot create new notes'); + }, 100_000); }); describe('importer uses autogenerated test contract interface', () => { diff --git a/yarn-project/noir-contracts/contracts/child_contract/Nargo.toml b/yarn-project/noir-contracts/contracts/child_contract/Nargo.toml index c3cd8225777..52ad24ae5a2 100644 --- a/yarn-project/noir-contracts/contracts/child_contract/Nargo.toml +++ b/yarn-project/noir-contracts/contracts/child_contract/Nargo.toml @@ -6,4 +6,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } +value_note = { path = "../../../aztec-nr/value-note" } diff --git a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr index 49caabe92cf..5d3e3ccb38d 100644 --- a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr @@ -6,16 +6,24 @@ contract Child { abi::CallContext, context::{PrivateContext, PublicContext, Context}, log::emit_unencrypted_log, - state_vars::public_state::PublicState, + state_vars::{ + public_state::PublicState, + set::Set, + }, + types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, }; use dep::aztec::protocol_types::{ abis::function_selector::FunctionSelector, address::AztecAddress, }; + use dep::value_note::{ + value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}, + }; struct Storage { current_value: PublicState, + a_private_value: Set } impl Storage { @@ -26,6 +34,11 @@ contract Child { 1, FieldSerializationMethods, ), + a_private_value: Set::new( + context, + 2, + ValueNoteMethods, + ), } } } @@ -74,6 +87,13 @@ contract Child { new_value } + #[aztec(private)] + fn privateSetValue(new_value: Field, owner: AztecAddress) -> Field { + let mut note = ValueNote::new(new_value, owner); + storage.a_private_value.insert(&mut note, true); + new_value + } + // Increments `current_value` by `new_value` #[aztec(public)] fn pubIncValue(new_value: Field) -> Field { diff --git a/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr b/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr index a19cea0d568..43b16815b78 100644 --- a/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr @@ -141,12 +141,25 @@ contract Parent { // Private function to call another private function in the targetContract using the provided selector #[aztec(private)] - fn privateStaticCall( + fn privateNoArgsStaticCall( targetContract: AztecAddress, targetSelector: FunctionSelector ) -> Field { // Call the target private function - let return_values = context.call_private_function_static(targetContract, targetSelector, [0]); + let return_values = context.call_private_function_no_args_static(targetContract, targetSelector); + + // Copy the return value from the call to this function's return values + return_values[0] + } + + #[aztec(private)] + fn privateStaticCall( + targetContract: AztecAddress, + targetSelector: FunctionSelector, + args: [Field; 2] + ) -> Field { + // Call the target private function + let return_values = context.call_private_function_static(targetContract, targetSelector, args); // Copy the return value from the call to this function's return values return_values[0] diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index f8180097e89..5ae775e29b9 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -46,7 +46,7 @@ import { PublicCallRequest, } from '@aztec/circuits.js'; import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/abis'; -import { DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; +import { DecodedReturn, encodeArguments } from '@aztec/foundation/abi'; import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { SerialQueue } from '@aztec/foundation/fifo'; @@ -208,13 +208,6 @@ export class PXEService implements PXE { } public async addContracts(contracts: DeployedContract[]) { - contracts.forEach(c => - console.log( - `Adding contract ${c.artifact.name} and selectors ${c.artifact.functions.map(f => - FunctionSelector.fromNameAndParameters(f.name, f.parameters), - )}`, - ), - ); const contractDaos = contracts.map(c => new ContractDao(c.artifact, c.completeAddress, c.portalContract)); await Promise.all(contractDaos.map(c => this.db.addContract(c))); for (const contract of contractDaos) { From 47a254c7260f5f053e3621275f61ad514f60af95 Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 2 Feb 2024 14:20:02 +0100 Subject: [PATCH 03/16] merge --- .github/workflows/protocol-circuits-gate-diff.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/protocol-circuits-gate-diff.yml b/.github/workflows/protocol-circuits-gate-diff.yml index 94ca8959b4f..e1e8b96b472 100644 --- a/.github/workflows/protocol-circuits-gate-diff.yml +++ b/.github/workflows/protocol-circuits-gate-diff.yml @@ -34,6 +34,7 @@ jobs: sudo cp -r clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04/include/* /usr/local/include/ sudo cp -r clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04/lib/* /usr/local/lib/ sudo cp -r clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04/share/* /usr/local/share/ + rm -rf clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04 - uses: actions/cache@v3 with: From c74857c307dd64e35a2a497376e8b4bda3d09ac5 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 6 Feb 2024 10:04:42 +0000 Subject: [PATCH 04/16] public static calls --- .../acir-simulator/src/acvm/oracle/oracle.ts | 4 ++ .../src/acvm/oracle/typed_oracle.ts | 2 + .../src/client/client_execution_context.ts | 14 +++- .../src/public/public_execution_context.ts | 19 +++++- yarn-project/aztec-nr/aztec/src/context.nr | 64 +++++++++++++++++-- .../oracle/enqueue_public_function_call.nr | 9 ++- .../aztec-nr/aztec/src/oracle/public_call.nr | 8 ++- .../src/e2e_nested_contract.test.ts | 16 ++++- .../contracts/child_contract/src/main.nr | 18 ++++-- .../contracts/parent_contract/src/main.nr | 17 +++-- 10 files changed, 139 insertions(+), 32 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 4e77a96d301..fd7cd2a30c1 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -300,11 +300,13 @@ export class Oracle { [contractAddress]: ACVMField[], [functionSelector]: ACVMField[], [argsHash]: ACVMField[], + [isStaticCall]: ACVMField[], ): Promise { const returnValues = await this.typedOracle.callPublicFunction( AztecAddress.fromField(fromACVMField(contractAddress)), FunctionSelector.fromField(fromACVMField(functionSelector)), fromACVMField(argsHash), + frToBoolean(fromACVMField(isStaticCall)), ); return padArrayEnd(returnValues, Fr.ZERO, RETURN_VALUES_LENGTH).map(toACVMField); } @@ -314,12 +316,14 @@ export class Oracle { [functionSelector]: ACVMField[], [argsHash]: ACVMField[], [sideffectCounter]: ACVMField[], + [isStaticCall]: ACVMField[], ) { const enqueuedRequest = await this.typedOracle.enqueuePublicFunctionCall( AztecAddress.fromString(contractAddress), FunctionSelector.fromField(fromACVMField(functionSelector)), fromACVMField(argsHash), frToNumber(fromACVMField(sideffectCounter)), + frToBoolean(fromACVMField(isStaticCall)), ); return toAcvmEnqueuePublicFunctionResult(enqueuedRequest); } diff --git a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts index 883b07ad54a..2767829ef7e 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -197,6 +197,7 @@ export abstract class TypedOracle { _targetContractAddress: AztecAddress, _functionSelector: FunctionSelector, _argsHash: Fr, + _isStaticCall: boolean, ): Promise { throw new Error('Not available.'); } @@ -206,6 +207,7 @@ export abstract class TypedOracle { _functionSelector: FunctionSelector, _argsHash: Fr, _sideffectCounter: number, + _isStaticCall: boolean, ): Promise { throw new Error('Not available.'); } diff --git a/yarn-project/acir-simulator/src/client/client_execution_context.ts b/yarn-project/acir-simulator/src/client/client_execution_context.ts index 7550ae50360..56be38bca11 100644 --- a/yarn-project/acir-simulator/src/client/client_execution_context.ts +++ b/yarn-project/acir-simulator/src/client/client_execution_context.ts @@ -294,6 +294,12 @@ export class ClientExecutionContext extends ViewDataOracle { this.log(`Emitted unencrypted log: "${log.toHumanReadable()}"`); } + checkValidStaticCall(childExecutionResult: ExecutionResult) { + if (childExecutionResult.newNotes.length > 0) { + throw new Error('Static call cannot create new notes'); + } + } + /** * Calls a private function as a nested execution. * @param targetContractAddress - The address of the contract to call. @@ -354,8 +360,8 @@ export class ClientExecutionContext extends ViewDataOracle { targetFunctionData, ); - if (childExecutionResult.newNotes.length > 0 && isStaticCall) { - throw new Error('Static call cannot create new notes'); + if (isStaticCall) { + this.checkValidStaticCall(childExecutionResult); } this.nestedExecutions.push(childExecutionResult); @@ -371,6 +377,7 @@ export class ClientExecutionContext extends ViewDataOracle { * @param functionSelector - The function selector of the function to call. * @param argsHash - The packed arguments to pass to the function. * @param sideEffectCounter - The side effect counter at the start of the call. + * @param isStaticCall - Whether the call is a static call. * @returns The public call stack item with the request information. */ public async enqueuePublicFunctionCall( @@ -378,6 +385,7 @@ export class ClientExecutionContext extends ViewDataOracle { functionSelector: FunctionSelector, argsHash: Fr, sideEffectCounter: number, + isStaticCall: boolean, ): Promise { const targetArtifact = await this.db.getFunctionArtifact(targetContractAddress, functionSelector); const derivedCallContext = await this.deriveCallContext( @@ -385,7 +393,7 @@ export class ClientExecutionContext extends ViewDataOracle { targetArtifact, sideEffectCounter, false, - false, + isStaticCall, ); const args = this.packedArgsCache.unpack(argsHash); const enqueuedRequest = PublicCallRequest.from({ diff --git a/yarn-project/acir-simulator/src/public/public_execution_context.ts b/yarn-project/acir-simulator/src/public/public_execution_context.ts index 1c275fae334..e846d5759ef 100644 --- a/yarn-project/acir-simulator/src/public/public_execution_context.ts +++ b/yarn-project/acir-simulator/src/public/public_execution_context.ts @@ -149,6 +149,17 @@ export class PublicExecutionContext extends TypedOracle { return newValues; } + checkValidStaticCall(childExecutionResult: PublicExecutionResult) { + if ( + childExecutionResult.contractStorageUpdateRequests.some( + updateRequest => !updateRequest.newValue.equals(updateRequest.oldValue), + ) || + childExecutionResult.newCommitments.length > 0 + ) { + throw new Error('Static call cannot updated the state'); + } + } + /** * Calls a public function as a nested execution. * @param targetContractAddress - The address of the contract to call. @@ -177,6 +188,8 @@ export class PublicExecutionContext extends TypedOracle { const functionData = new FunctionData(functionSelector, isInternal, false, false); + const isStaticCall = this.execution.callContext.isStaticCall; + const callContext = CallContext.from({ msgSender: this.execution.contractAddress, portalContractAddress: portalAddress, @@ -184,7 +197,7 @@ export class PublicExecutionContext extends TypedOracle { functionSelector, isContractDeployment: false, isDelegateCall: false, - isStaticCall: false, + isStaticCall, startSideEffectCounter: 0, // TODO use counters in public execution }); @@ -209,6 +222,10 @@ export class PublicExecutionContext extends TypedOracle { const childExecutionResult = await executePublicFunction(context, acir); + if (isStaticCall) { + this.checkValidStaticCall(childExecutionResult); + } + this.nestedExecutions.push(childExecutionResult); this.log(`Returning from nested call: ret=${childExecutionResult.returnValues.join(', ')}`); diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index 2d8ac07d6dd..86a0a647d36 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -368,11 +368,22 @@ impl PrivateContext { &mut self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + 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, false) + } + + pub fn call_public_function_static( + &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) + self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, true) } pub fn call_public_function_no_args( @@ -380,20 +391,30 @@ impl PrivateContext { contract_address: AztecAddress, function_selector: FunctionSelector, ) { - self.call_public_function_with_packed_args(contract_address, function_selector, 0) + self.call_public_function_with_packed_args(contract_address, function_selector, 0, false) + } + + pub fn call_public_function_no_args_static( + &mut self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + ) { + self.call_public_function_with_packed_args(contract_address, function_selector, 0, true) } pub fn call_public_function_with_packed_args( &mut self, contract_address: AztecAddress, function_selector: FunctionSelector, - args_hash: Field + args_hash: Field, + is_static_call: bool ) { let fields = enqueue_public_function_call_internal( contract_address, function_selector, args_hash, - self.side_effect_counter + self.side_effect_counter, + is_static_call, ); let mut reader = Reader::new(fields); @@ -436,7 +457,7 @@ impl PrivateContext { // 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_static_call == is_static_call); 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)); @@ -617,6 +638,23 @@ impl PublicContext { contract_address, function_selector, args_hash, + false, + ) + } + + pub fn call_public_function_static( + _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, + true, ) } @@ -629,6 +667,20 @@ impl PublicContext { contract_address, function_selector, 0, + false, + ) + } + + pub fn call_public_function_no_args_static( + _self: Self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + ) -> [Field; RETURN_VALUES_LENGTH] { + call_public_function_internal( + contract_address, + function_selector, + 0, + true, ) } diff --git a/yarn-project/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr b/yarn-project/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr index d48f6b2ab63..b4a7e959c88 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr @@ -15,19 +15,22 @@ fn enqueue_public_function_call_oracle( _contract_address: AztecAddress, _function_selector: FunctionSelector, _args_hash: Field, - _side_effect_counter: u32 + _side_effect_counter: u32, + _is_static_call: bool ) -> [Field; ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE] {} unconstrained pub fn enqueue_public_function_call_internal( contract_address: AztecAddress, function_selector: FunctionSelector, args_hash: Field, - side_effect_counter: u32 + side_effect_counter: u32, + is_static_call: bool ) -> [Field; ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE] { enqueue_public_function_call_oracle( contract_address, function_selector, args_hash, - side_effect_counter + side_effect_counter, + is_static_call ) } diff --git a/yarn-project/aztec-nr/aztec/src/oracle/public_call.nr b/yarn-project/aztec-nr/aztec/src/oracle/public_call.nr index 0bc81973675..bff97993161 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/public_call.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/public_call.nr @@ -8,13 +8,15 @@ use dep::protocol_types::{ fn call_public_function_oracle( _contract_address: AztecAddress, _function_selector: FunctionSelector, - _args_hash: Field + _args_hash: Field, + _is_static_call: bool ) -> [Field; RETURN_VALUES_LENGTH] {} unconstrained pub fn call_public_function_internal( contract_address: AztecAddress, function_selector: FunctionSelector, - args_hash: Field + args_hash: Field, + is_static_call: bool ) -> [Field; RETURN_VALUES_LENGTH] { - call_public_function_oracle(contract_address, function_selector, args_hash) + call_public_function_oracle(contract_address, function_selector, args_hash, is_static_call) } diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index f8b0eebebec..179caa99f7a 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -153,12 +153,22 @@ describe('e2e_nested_contract', () => { it('performs legal private static calls', async () => { await parentContract.methods - .privateNoArgsStaticCall(childContract.address, childContract.methods.value.selector) + .privateStaticCall(childContract.address, childContract.methods.privateGetValue.selector, [ + 42n, + wallet.getCompleteAddress().address, + ]) .send() .wait(); }, 100_000); - it('fails when performing illegal static calls', async () => { + it('performs legal public static calls', async () => { + await parentContract.methods + .pubEntryPoint(childContract.address, childContract.methods.pubGetValue.selector, 42n) + .send() + .wait(); + }, 100_000); + + it('fails when performing illegal private static calls', async () => { await expect( parentContract.methods .privateStaticCall(childContract.address, childContract.methods.privateSetValue.selector, [ @@ -167,7 +177,7 @@ describe('e2e_nested_contract', () => { ]) .send() .wait(), - ).rejects.toThrowError('Static call cannot create new notes'); + ).rejects.toThrow('Static call cannot create new notes'); }, 100_000); }); diff --git a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr index bf7a63710c1..ee0ee049849 100644 --- a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr @@ -9,12 +9,15 @@ contract Child { state_vars::{ public_state::PublicState, set::Set + }, + protocol_types::{ + abis::function_selector::FunctionSelector, + address::AztecAddress, + }, + note::{ + note_getter_options::NoteGetterOptions, } }; - use dep::aztec::protocol_types::{ - abis::function_selector::FunctionSelector, - address::AztecAddress, - }; use dep::value_note::{ value_note::{ValueNote, VALUE_NOTE_LEN}, }; @@ -75,6 +78,13 @@ contract Child { new_value } + #[aztec(private)] + fn privateGetValue(amount: Field, owner: AztecAddress) -> Field { + let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, owner.to_field(), Option::none()).set_limit(1); + let notes = storage.a_private_value.get_notes(options); + notes[0].unwrap_unchecked().value + } + // Increments `current_value` by `new_value` #[aztec(public)] fn pubIncValue(new_value: Field) -> Field { diff --git a/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr b/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr index 43b16815b78..cdf1078795f 100644 --- a/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr @@ -139,29 +139,28 @@ contract Parent { ); } - // Private function to call another private function in the targetContract using the provided selector #[aztec(private)] - fn privateNoArgsStaticCall( + fn privateStaticCall( targetContract: AztecAddress, - targetSelector: FunctionSelector + targetSelector: FunctionSelector, + args: [Field; 2] ) -> Field { // Call the target private function - let return_values = context.call_private_function_no_args_static(targetContract, targetSelector); + let return_values = context.call_private_function_static(targetContract, targetSelector, args); // Copy the return value from the call to this function's return values return_values[0] } - #[aztec(private)] - fn privateStaticCall( + // Public function to directly call another public function to the targetContract using the selector and value provided + #[aztec(public)] + fn publicStaticCall( targetContract: AztecAddress, targetSelector: FunctionSelector, args: [Field; 2] ) -> Field { - // Call the target private function - let return_values = context.call_private_function_static(targetContract, targetSelector, args); + let return_values = context.call_public_function_static(targetContract, targetSelector, args); - // Copy the return value from the call to this function's return values return_values[0] } } From 457245de76e3fefe8e9a2c4af282473c732a79c2 Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 7 Feb 2024 09:29:13 +0000 Subject: [PATCH 05/16] working static calls in aztec.js --- .../src/defaults/account_entrypoint.ts | 6 + .../src/defaults/entrypoint_payload.ts | 6 + .../acir-simulator/src/client/simulator.ts | 1 + .../client/unconstrained_execution.test.ts | 1 + .../acir-simulator/src/public/execution.ts | 15 ++ .../acir-simulator/src/public/executor.ts | 16 ++- .../src/public/public_execution_context.ts | 22 +-- .../aztec-nr/authwit/src/entrypoint.nr | 20 +-- .../aztec-nr/aztec/src/context/private.nr | 4 +- .../contract/contract_function_interaction.ts | 28 +++- .../circuit-types/src/function_call.ts | 3 + .../end-to-end/src/e2e_static_calls.test.ts | 135 ++++++++++++++++++ .../contracts/parent_contract/src/main.nr | 13 +- .../pxe/src/pxe_service/pxe_service.ts | 10 +- .../src/sequencer/public_processor.test.ts | 2 + 15 files changed, 247 insertions(+), 35 deletions(-) create mode 100644 yarn-project/end-to-end/src/e2e_static_calls.test.ts diff --git a/yarn-project/accounts/src/defaults/account_entrypoint.ts b/yarn-project/accounts/src/defaults/account_entrypoint.ts index 782d5c4119a..521ad447f6c 100644 --- a/yarn-project/accounts/src/defaults/account_entrypoint.ts +++ b/yarn-project/accounts/src/defaults/account_entrypoint.ts @@ -101,6 +101,12 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { kind: 'boolean', }, }, + { + name: 'is_static', + type: { + kind: 'boolean', + }, + }, ], }, }, diff --git a/yarn-project/accounts/src/defaults/entrypoint_payload.ts b/yarn-project/accounts/src/defaults/entrypoint_payload.ts index 96461519bfb..61d4db6f96f 100644 --- a/yarn-project/accounts/src/defaults/entrypoint_payload.ts +++ b/yarn-project/accounts/src/defaults/entrypoint_payload.ts @@ -20,6 +20,9 @@ type EntrypointFunctionCall = { // eslint-disable-next-line camelcase /** Whether the function is public or private */ is_public: boolean; + // eslint-disable-next-line camelcase + /** Whether the function is able to modify state */ + is_static: boolean; }; /** Encoded payload for the account contract entrypoint */ @@ -55,6 +58,8 @@ export function buildPayload(calls: FunctionCall[]): { target_address: call.to.toField(), // eslint-disable-next-line camelcase is_public: !call.functionData.isPrivate, + // eslint-disable-next-line camelcase + is_static: call.static, })); return { @@ -83,6 +88,7 @@ function flattenPayload(payload: EntrypointPayload) { call.function_selector, call.target_address, new Fr(call.is_public), + new Fr(call.is_static), ]), payload.nonce, ]; diff --git a/yarn-project/acir-simulator/src/client/simulator.ts b/yarn-project/acir-simulator/src/client/simulator.ts index be2852dce69..0bba80e6880 100644 --- a/yarn-project/acir-simulator/src/client/simulator.ts +++ b/yarn-project/acir-simulator/src/client/simulator.ts @@ -186,6 +186,7 @@ export class AcirSimulator { to: contractAddress, functionData: FunctionData.empty(), args: encodeArguments(artifact, [contractAddress, nonce, storageSlot, extendedNoteItems]), + static: false, }; const [innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier] = (await this.runUnconstrained( diff --git a/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts b/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts index 68b7ba6764c..15f5bd7dd74 100644 --- a/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts @@ -65,6 +65,7 @@ describe('Unconstrained Execution test suite', () => { to: contractAddress, functionData: new FunctionData(FunctionSelector.empty(), false, true, true), args: encodeArguments(artifact, [owner]), + static: false, }; const result = await acirSimulator.runUnconstrained(execRequest, artifact, AztecAddress.random()); diff --git a/yarn-project/acir-simulator/src/public/execution.ts b/yarn-project/acir-simulator/src/public/execution.ts index c6df2312923..24286f6630c 100644 --- a/yarn-project/acir-simulator/src/public/execution.ts +++ b/yarn-project/acir-simulator/src/public/execution.ts @@ -136,3 +136,18 @@ function contractStorageUpdateRequestToPublicDataUpdateRequest( update.sideEffectCounter!, ); } + +/** + * Checks whether the child execution result is valid for a static call (no state modifications). + * @param executionResult - The execution result of a public function + */ + +export function checkValidStaticCall( + newCommitments: SideEffect[], + newNullifiers: SideEffectLinkedToNoteHash[], + contractStorageUpdateRequests: ContractStorageUpdateRequest[], +) { + if (contractStorageUpdateRequests.length > 0 || newCommitments.length > 0 || newNullifiers.length > 0) { + throw new Error('Static call cannot update the state'); + } +} diff --git a/yarn-project/acir-simulator/src/public/executor.ts b/yarn-project/acir-simulator/src/public/executor.ts index e5d3b935403..45f6193b5cf 100644 --- a/yarn-project/acir-simulator/src/public/executor.ts +++ b/yarn-project/acir-simulator/src/public/executor.ts @@ -7,7 +7,7 @@ import { SideEffectCounter } from '../common/index.js'; import { PackedArgsCache } from '../common/packed_args_cache.js'; import { AcirSimulator } from '../index.js'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js'; -import { PublicExecution, PublicExecutionResult } from './execution.js'; +import { PublicExecution, PublicExecutionResult, checkValidStaticCall } from './execution.js'; import { PublicExecutionContext } from './public_execution_context.js'; /** @@ -115,10 +115,22 @@ export class PublicExecutor { this.commitmentsDb, ); + let executionResult; + try { - return await executePublicFunction(context, acir); + executionResult = await executePublicFunction(context, acir); } catch (err) { throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during public execution')); } + + if (executionResult.execution.callContext.isStaticCall) { + checkValidStaticCall( + executionResult.newCommitments, + executionResult.newNullifiers, + executionResult.contractStorageUpdateRequests, + ); + } + + return executionResult; } } diff --git a/yarn-project/acir-simulator/src/public/public_execution_context.ts b/yarn-project/acir-simulator/src/public/public_execution_context.ts index e846d5759ef..c77a661f00e 100644 --- a/yarn-project/acir-simulator/src/public/public_execution_context.ts +++ b/yarn-project/acir-simulator/src/public/public_execution_context.ts @@ -8,7 +8,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { TypedOracle, toACVMWitness } from '../acvm/index.js'; import { PackedArgsCache, SideEffectCounter } from '../common/index.js'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js'; -import { PublicExecution, PublicExecutionResult } from './execution.js'; +import { PublicExecution, PublicExecutionResult, checkValidStaticCall } from './execution.js'; import { executePublicFunction } from './executor.js'; import { ContractStorageActionsCollector } from './state_actions.js'; @@ -149,17 +149,6 @@ export class PublicExecutionContext extends TypedOracle { return newValues; } - checkValidStaticCall(childExecutionResult: PublicExecutionResult) { - if ( - childExecutionResult.contractStorageUpdateRequests.some( - updateRequest => !updateRequest.newValue.equals(updateRequest.oldValue), - ) || - childExecutionResult.newCommitments.length > 0 - ) { - throw new Error('Static call cannot updated the state'); - } - } - /** * Calls a public function as a nested execution. * @param targetContractAddress - The address of the contract to call. @@ -171,6 +160,7 @@ export class PublicExecutionContext extends TypedOracle { targetContractAddress: AztecAddress, functionSelector: FunctionSelector, argsHash: Fr, + isStaticCall: boolean, ) { const args = this.packedArgsCache.unpack(argsHash); this.log(`Public function call: addr=${targetContractAddress} selector=${functionSelector} args=${args.join(',')}`); @@ -188,8 +178,6 @@ export class PublicExecutionContext extends TypedOracle { const functionData = new FunctionData(functionSelector, isInternal, false, false); - const isStaticCall = this.execution.callContext.isStaticCall; - const callContext = CallContext.from({ msgSender: this.execution.contractAddress, portalContractAddress: portalAddress, @@ -223,7 +211,11 @@ export class PublicExecutionContext extends TypedOracle { const childExecutionResult = await executePublicFunction(context, acir); if (isStaticCall) { - this.checkValidStaticCall(childExecutionResult); + checkValidStaticCall( + childExecutionResult.newCommitments, + childExecutionResult.newNullifiers, + childExecutionResult.contractStorageUpdateRequests, + ); } this.nestedExecutions.push(childExecutionResult); diff --git a/yarn-project/aztec-nr/authwit/src/entrypoint.nr b/yarn-project/aztec-nr/authwit/src/entrypoint.nr index 65d2f79d773..aaff8849548 100644 --- a/yarn-project/aztec-nr/authwit/src/entrypoint.nr +++ b/yarn-project/aztec-nr/authwit/src/entrypoint.nr @@ -8,21 +8,22 @@ use dep::aztec::protocol_types::{ }; global ACCOUNT_MAX_CALLS: Field = 4; -// 1 (ARGS_HASH) + 1 (FUNCTION_SELECTOR) + 1 (TARGET_ADDRESS) + 1 (IS_PUBLIC) -global FUNCTION_CALL_SIZE: Field = 4; -// 3 * 32 + 1 -global FUNCTION_CALL_SIZE_IN_BYTES: Field = 97; +// 1 (ARGS_HASH) + 1 (FUNCTION_SELECTOR) + 1 (TARGET_ADDRESS) + 1 (IS_PUBLIC) + 1 (IS_STATIC) +global FUNCTION_CALL_SIZE: Field = 5; +// 3 * 32 + 2 +global FUNCTION_CALL_SIZE_IN_BYTES: Field = 98; struct FunctionCall { args_hash: Field, function_selector: FunctionSelector, target_address: AztecAddress, is_public: bool, + is_static: bool, } impl Serialize for FunctionCall { fn serialize(self) -> [Field; FUNCTION_CALL_SIZE] { - [self.args_hash, self.function_selector.to_field(), self.target_address.to_field(), self.is_public as Field] + [self.args_hash, self.function_selector.to_field(), self.target_address.to_field(), self.is_public as Field, self.is_static as Field] } } @@ -36,14 +37,15 @@ impl FunctionCall { let target_address_bytes = self.target_address.to_field().to_be_bytes(32); for i in 0..32 { bytes[i + 64] = target_address_bytes[i]; } bytes[96] = self.is_public as u8; + bytes[97] = self.is_static as u8; bytes } } // FUNCTION_CALL_SIZE * ACCOUNT_MAX_CALLS + 1 -global ENTRYPOINT_PAYLOAD_SIZE: Field = 17; +global ENTRYPOINT_PAYLOAD_SIZE: Field = 21; // FUNCTION_CALL_SIZE_IN_BYTES * ACCOUNT_MAX_CALLS + 32 -global ENTRYPOINT_PAYLOAD_SIZE_IN_BYTES: Field = 420; +global ENTRYPOINT_PAYLOAD_SIZE_IN_BYTES: Field = 424; // Note: If you change the following struct you have to update default_entrypoint.ts // docs:start:entrypoint-struct @@ -102,11 +104,11 @@ impl EntrypointPayload { if !call.target_address.is_zero() { if call.is_public { context.call_public_function_with_packed_args( - call.target_address, call.function_selector, call.args_hash + call.target_address, call.function_selector, call.args_hash, call.is_static ); } else { let _result = context.call_private_function_with_packed_args( - call.target_address, call.function_selector, call.args_hash, false + call.target_address, call.function_selector, call.args_hash, call.is_static ); } } diff --git a/yarn-project/aztec-nr/aztec/src/context/private.nr b/yarn-project/aztec-nr/aztec/src/context/private.nr index acc25a3c7d6..b94936fc203 100644 --- a/yarn-project/aztec-nr/aztec/src/context/private.nr +++ b/yarn-project/aztec-nr/aztec/src/context/private.nr @@ -336,7 +336,7 @@ impl PrivateContext { // 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_static_call == is_static_call); 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)); @@ -439,7 +439,7 @@ impl PrivateContext { // 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_static_call == is_static_call); 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)); diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index 891ffb08a62..b6af30c4915 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -18,6 +18,17 @@ export type ViewMethodOptions = { from?: AztecAddress; }; +/** + * Options for sending a function execution request to a contract. + * Allows specifying whether it is a static call or not, and additional send method options. + */ +export type ExecutionRequestOptions = { + /** + * Wether the execution is static, i.e. it does not modify state + */ + static?: boolean; +} & SendMethodOptions; + /** * This is the class that is returned when calling e.g. `contract.methods.myMethod(arg0, arg1)`. * It contains available interactions one can call on a method, including view. @@ -35,17 +46,26 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } } + /** + * Custom send supporting extended options + * @param options - An optional object containing additional configuration for the transaction. + * */ + public send(options: ExecutionRequestOptions = {}) { + return super.send(options); + } + /** * Create a transaction execution request that represents this call, encoded and authenticated by the * user's wallet, ready to be simulated. + * @param options - An optional object containing additional configuration for the transaction. * @returns A Promise that resolves to a transaction instance. */ - public async create(): Promise { + public async create(options: ExecutionRequestOptions = {}): Promise { if (this.functionDao.functionType === FunctionType.UNCONSTRAINED) { throw new Error("Can't call `create` on an unconstrained function."); } if (!this.txRequest) { - this.txRequest = await this.wallet.createTxExecutionRequest([this.request()]); + this.txRequest = await this.wallet.createTxExecutionRequest([this.request(options)]); } return this.txRequest; } @@ -56,10 +76,10 @@ export class ContractFunctionInteraction extends BaseContractInteraction { * @param options - An optional object containing additional configuration for the transaction. * @returns An execution request wrapped in promise. */ - public request(): FunctionCall { + public request(options: ExecutionRequestOptions = {}): FunctionCall { const args = encodeArguments(this.functionDao, this.args); const functionData = FunctionData.fromAbi(this.functionDao); - return { args, functionData, to: this.contractAddress }; + return { args, functionData, to: this.contractAddress, static: options.static ?? false }; } /** diff --git a/yarn-project/circuit-types/src/function_call.ts b/yarn-project/circuit-types/src/function_call.ts index c3d8e831ad4..e471201683f 100644 --- a/yarn-project/circuit-types/src/function_call.ts +++ b/yarn-project/circuit-types/src/function_call.ts @@ -8,6 +8,8 @@ export type FunctionCall = { functionData: FunctionData; /** The encoded args */ args: Fr[]; + /** Whether it's able to alter state or not */ + static: boolean; }; /** @@ -19,5 +21,6 @@ export function emptyFunctionCall() { to: AztecAddress.ZERO, functionData: FunctionData.empty(), args: [], + static: false, }; } diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts new file mode 100644 index 00000000000..e77d6938377 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -0,0 +1,135 @@ +import { Wallet } from '@aztec/aztec.js'; +import { getTestData, isGenerateTestDataEnabled } from '@aztec/foundation/testing'; +import { ChildContract, ParentContract } from '@aztec/noir-contracts'; + +import { writeFileSync } from 'fs'; + +import { setup } from './fixtures/utils.js'; + +describe('e2e_static_calls', () => { + let wallet: Wallet; + let parentContract: ParentContract; + let childContract: ChildContract; + let teardown: () => Promise; + + beforeEach(async () => { + ({ teardown, wallet } = await setup()); + }, 100_000); + + afterEach(() => teardown()); + + beforeEach(async () => { + parentContract = await ParentContract.deploy(wallet).send().deployed(); + childContract = await ChildContract.deploy(wallet).send().deployed(); + }, 100_000); + + describe('direct calls', () => { + it('performs legal private static calls', async () => { + await childContract.methods + .privateGetValue(42n, wallet.getCompleteAddress().address) + .send({ static: true }) + .wait(); + + if (isGenerateTestDataEnabled()) { + { + const privateKernelInputsInit = getTestData('private-kernel-inputs-init'); + const nestedCallPrivateKernelInput = privateKernelInputsInit[0]; + writeFileSync( + '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-init.hex', + nestedCallPrivateKernelInput.toBuffer().toString('hex'), + ); + } + + { + const privateKernelInputsInner = getTestData('private-kernel-inputs-inner'); + const nestedCallPrivateKernelInput = privateKernelInputsInner[privateKernelInputsInner.length - 1]; + writeFileSync( + '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-inner.hex', + nestedCallPrivateKernelInput.toBuffer().toString('hex'), + ); + } + + { + const privateKernelInputsOrdering = getTestData('private-kernel-inputs-ordering'); + const nestedCallPrivateKernelInput = privateKernelInputsOrdering[0]; + writeFileSync( + '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-ordering.hex', + nestedCallPrivateKernelInput.toBuffer().toString('hex'), + ); + } + } + }, 100_000); + + it('performs legal public static calls', async () => { + await childContract.methods.pubGetValue(42n).send({ static: true }).wait(); + }, 100_000); + + it('fails when performing illegal private static calls', async () => { + await expect( + childContract.methods.privateSetValue(42n, wallet.getCompleteAddress().address).send({ static: true }).wait(), + ).rejects.toThrow('Static call cannot create new notes'); + }, 100_000); + + it('fails when performing illegal public static calls', async () => { + await expect(childContract.methods.pubSetValue(42n).send({ static: true }).wait()).rejects.toThrow( + 'Static call cannot update the state', + ); + }, 100_000); + }); + + describe('parent calls child', () => { + it('performs legal private to private static calls', async () => { + await parentContract.methods + .privateStaticCall(childContract.address, childContract.methods.privateGetValue.selector, [ + 42n, + wallet.getCompleteAddress().address, + ]) + .send() + .wait(); + }, 100_000); + + it('performs legal public to public static calls', async () => { + await parentContract.methods + .enqueueStaticCallToPubFunction(childContract.address, childContract.methods.pubGetValue.selector, [42n]) + .send() + .wait(); + }, 100_000); + + it('performs legal enqueued public static calls', async () => { + await parentContract.methods + .publicStaticCall(childContract.address, childContract.methods.pubGetValue.selector, [42n]) + .send() + .wait(); + }, 100_000); + + it('fails when performing illegal private to private static calls', async () => { + await expect( + parentContract.methods + .privateStaticCall(childContract.address, childContract.methods.privateSetValue.selector, [ + 42n, + wallet.getCompleteAddress().address, + ]) + .send() + .wait(), + ).rejects.toThrow('Static call cannot create new notes'); + }, 100_000); + + it('fails when performing illegal public to public static calls', async () => { + await expect( + parentContract.methods + .publicStaticCall(childContract.address, childContract.methods.pubSetValue.selector, [42n]) + .send() + .wait(), + ).rejects.toThrow('Static call cannot update the state'); + }, 100_000); + + it('fails when performing illegal enqueued public static calls', async () => { + await expect( + parentContract.methods + .enqueueStaticCallToPubFunction(childContract.address, childContract.methods.pubSetValue.selector, [42n]) + .send() + .wait(), + ).rejects.toThrow('Static call cannot update the state'); + }, 100_000); + }); +}); diff --git a/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr b/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr index cdf1078795f..c3619e321e2 100644 --- a/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/parent_contract/src/main.nr @@ -157,10 +157,21 @@ contract Parent { fn publicStaticCall( targetContract: AztecAddress, targetSelector: FunctionSelector, - args: [Field; 2] + args: [Field; 1] ) -> Field { let return_values = context.call_public_function_static(targetContract, targetSelector, args); return_values[0] } + + // Private function to enqueue a static call to the pubEntryPoint function of another contract, passing the target arguments provided + #[aztec(private)] + fn enqueueStaticCallToPubFunction( + targetContract: AztecAddress, + targetSelector: FunctionSelector, + args: [Field; 1] + ) { + // Call the target private function + context.call_public_function_static(targetContract, targetSelector, args); + } } diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index a90970194bf..189c6bd280b 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -416,7 +416,7 @@ export class PXEService implements PXE { // all simulations must be serialized w.r.t. the synchronizer return await this.jobQueue.put(async () => { // TODO - Should check if `from` has the permission to call the view function. - const functionCall = await this.#getFunctionCall(functionName, args, to); + const functionCall = await this.#getFunctionCall(functionName, args, to, false); const executionResult = await this.#simulateUnconstrained(functionCall); // TODO - Return typed result based on the function artifact. @@ -479,7 +479,12 @@ export class PXEService implements PXE { return this.node.getUnencryptedLogs(filter); } - async #getFunctionCall(functionName: string, args: any[], to: AztecAddress): Promise { + async #getFunctionCall( + functionName: string, + args: any[], + to: AztecAddress, + isStatic: boolean, + ): Promise { const contract = await this.db.getContract(to); if (!contract) { throw new Error( @@ -496,6 +501,7 @@ export class PXEService implements PXE { args: encodeArguments(functionDao, args), functionData: FunctionData.fromAbi(functionDao), to, + static: isStatic, }; } diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts index 56c9baff1fe..62818ab566e 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -215,6 +215,7 @@ describe('public_processor', () => { to: makeAztecAddress(30), functionData: new FunctionData(makeSelector(5), false, false, false), args: new Array(ARGS_LENGTH).fill(Fr.ZERO), + static: false, }), ]; publicExecutor.simulate.mockResolvedValue(publicExecutionResult); @@ -256,6 +257,7 @@ describe('public_processor', () => { to: makeAztecAddress(30), functionData: new FunctionData(makeSelector(5), false, false, false), args: new Array(ARGS_LENGTH).fill(Fr.ZERO), + static: false, }), ]; publicExecutor.simulate.mockRejectedValueOnce(new SimulationError('Simulation Failed', [])); From 21de063d3fafa106ec0a7e994d42180c210b252c Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 7 Feb 2024 09:34:58 +0000 Subject: [PATCH 06/16] removed duplicate tests --- .../src/e2e_nested_contract.test.ts | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index 179caa99f7a..d3dec0751ea 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -150,35 +150,6 @@ describe('e2e_nested_contract', () => { expect(processedLogs).toEqual([20n, 40n]); expect(await getChildStoredValue(childContract)).toEqual(new Fr(40n)); }); - - it('performs legal private static calls', async () => { - await parentContract.methods - .privateStaticCall(childContract.address, childContract.methods.privateGetValue.selector, [ - 42n, - wallet.getCompleteAddress().address, - ]) - .send() - .wait(); - }, 100_000); - - it('performs legal public static calls', async () => { - await parentContract.methods - .pubEntryPoint(childContract.address, childContract.methods.pubGetValue.selector, 42n) - .send() - .wait(); - }, 100_000); - - it('fails when performing illegal private static calls', async () => { - await expect( - parentContract.methods - .privateStaticCall(childContract.address, childContract.methods.privateSetValue.selector, [ - 42n, - wallet.getCompleteAddress().address, - ]) - .send() - .wait(), - ).rejects.toThrow('Static call cannot create new notes'); - }, 100_000); }); describe('importer uses autogenerated test contract interface', () => { From 373dae6a8facec897492d0b5807735bb2279798b Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 7 Feb 2024 09:51:57 +0000 Subject: [PATCH 07/16] added tests to CI --- .circleci/config.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b036b70684a..a8a605f0bd0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -654,6 +654,17 @@ jobs: name: "Test" command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_nested_contract.test.ts + e2e-static-calls: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small + steps: + - *checkout + - *setup_env + - run: + name: "Test" + command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_static_calls.test.ts + e2e-non-contract-account: docker: - image: aztecprotocol/alpine-build-image From 5a20896ae7774a4c4614f43590af71280a5314fd Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 7 Feb 2024 10:05:40 +0000 Subject: [PATCH 08/16] properly added new e2e test. probably --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index a8a605f0bd0..b90c690a7ed 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1269,6 +1269,7 @@ workflows: - e2e-state-vars: *e2e_test - e2e-block-building: *e2e_test - e2e-nested-contract: *e2e_test + - e2e-static-calls: *e2e_test - e2e-non-contract-account: *e2e_test - e2e-multiple-accounts-1-enc-key: *e2e_test - e2e-cli: *e2e_test @@ -1307,6 +1308,7 @@ workflows: - e2e-state-vars - e2e-block-building - e2e-nested-contract + - e2e-static-calls - e2e-non-contract-account - e2e-multiple-accounts-1-enc-key - e2e-cli From a5753ae253a565e2ccd20f87dfd0e968d825e2c8 Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 7 Feb 2024 19:44:49 +0000 Subject: [PATCH 09/16] added static support to simulate --- .../src/contract/contract_function_interaction.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index b6af30c4915..5fb523362f6 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -54,6 +54,14 @@ export class ContractFunctionInteraction extends BaseContractInteraction { return super.send(options); } + /** + * Custom simulate supporting extended options + * @param options - An optional object containing additional configuration for the transaction. + * */ + public simulate(options: ExecutionRequestOptions = {}) { + return super.simulate(options); + } + /** * Create a transaction execution request that represents this call, encoded and authenticated by the * user's wallet, ready to be simulated. From 954c8755c5eed0442f3942bd31adbf99535b4a10 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 12 Feb 2024 10:23:32 +0000 Subject: [PATCH 10/16] logs and l2-l1 messages checks --- .../contracts/child_contract/src/main.nr | 6 ++++-- .../src/crates/private-kernel-lib/src/common.nr | 11 +++++++++++ .../src/crates/public-kernel-lib/src/common.nr | 7 +++++++ .../simulator/src/client/client_execution_context.ts | 8 ++++++-- yarn-project/simulator/src/public/execution.ts | 10 +++++++++- yarn-project/simulator/src/public/executor.ts | 2 ++ .../simulator/src/public/public_execution_context.ts | 2 ++ 7 files changed, 41 insertions(+), 5 deletions(-) diff --git a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr index 6e45895a62b..b625b394f1b 100644 --- a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr @@ -10,7 +10,10 @@ contract Child { set::Set }, protocol_types::{ - abis::function_selector::FunctionSelector, + abis::{ + function_selector::FunctionSelector, + call_context::CallContext + }, address::AztecAddress, }, note::{ @@ -19,7 +22,6 @@ contract Child { }; use dep::value_note::value_note::ValueNote; - struct Storage { current_value: PublicState, a_private_value: Set, diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr index 49c833d37a8..5dd15733f18 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr @@ -141,6 +141,17 @@ fn perform_static_call_checks(private_call: PrivateCallData) { assert( is_empty_array(public_inputs.new_nullifiers), "new_nullifiers must be empty for static calls" ); + + let new_l2_to_l1_msgs_length = array_length(public_inputs.new_l2_to_l1_msgs); + assert(new_l2_to_l1_msgs_length == 0, "new_l2_to_l1_msgs must be empty for static calls"); + + assert( + public_inputs.encrypted_log_preimages_length == 0, "No unencrypted logs are allowed for static calls" + ); + + assert( + public_inputs.unencrypted_log_preimages_length == 0, "No unencrypted logs are allowed for static calls" + ); } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr index 7bb3a644a2c..f61e019df4f 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr @@ -95,6 +95,13 @@ fn perform_static_call_checks(public_call: PublicCallData) { assert( update_requests_length == 0, "No contract storage update requests are allowed for static calls" ); + + let new_l2_to_l1_msgs_length = array_length(public_inputs.new_l2_to_l1_msgs); + assert(new_l2_to_l1_msgs_length == 0, "new_l2_to_l1_msgs must be empty for static calls"); + + assert( + public_inputs.unencrypted_log_preimages_length == 0, "No unencrypted logs are allowed for static calls" + ); } } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 3e3d8536457..a50388ca2fb 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -305,8 +305,12 @@ export class ClientExecutionContext extends ViewDataOracle { } #checkValidStaticCall(childExecutionResult: ExecutionResult) { - if (childExecutionResult.newNotes.length > 0) { - throw new Error('Static call cannot create new notes'); + if ( + childExecutionResult.newNotes.length > 0 || + childExecutionResult.encryptedLogs.logs.length == 0 || + childExecutionResult.unencryptedLogs.logs.length > 0 + ) { + throw new Error('Static call cannot create new notes or generate logs'); } } diff --git a/yarn-project/simulator/src/public/execution.ts b/yarn-project/simulator/src/public/execution.ts index 24286f6630c..dbf298d637d 100644 --- a/yarn-project/simulator/src/public/execution.ts +++ b/yarn-project/simulator/src/public/execution.ts @@ -146,8 +146,16 @@ export function checkValidStaticCall( newCommitments: SideEffect[], newNullifiers: SideEffectLinkedToNoteHash[], contractStorageUpdateRequests: ContractStorageUpdateRequest[], + newL2ToL1Messages: Fr[], + unencryptedLogs: FunctionL2Logs, ) { - if (contractStorageUpdateRequests.length > 0 || newCommitments.length > 0 || newNullifiers.length > 0) { + if ( + contractStorageUpdateRequests.length > 0 || + newCommitments.length > 0 || + newNullifiers.length > 0 || + newL2ToL1Messages.length > 0 || + unencryptedLogs.logs.length > 0 + ) { throw new Error('Static call cannot update the state'); } } diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts index b47f3322fc8..ad15149e3f6 100644 --- a/yarn-project/simulator/src/public/executor.ts +++ b/yarn-project/simulator/src/public/executor.ts @@ -137,6 +137,8 @@ export class PublicExecutor { executionResult.newCommitments, executionResult.newNullifiers, executionResult.contractStorageUpdateRequests, + executionResult.newL2ToL1Messages, + executionResult.unencryptedLogs, ); } diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index c77a661f00e..fd18d7622f1 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -215,6 +215,8 @@ export class PublicExecutionContext extends TypedOracle { childExecutionResult.newCommitments, childExecutionResult.newNullifiers, childExecutionResult.contractStorageUpdateRequests, + childExecutionResult.newL2ToL1Messages, + childExecutionResult.unencryptedLogs, ); } From 52764808f94ef17522e3433ad98255d5b0d9c036 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 12 Feb 2024 12:05:00 +0000 Subject: [PATCH 11/16] more checks and moved to simulate call --- .../contract/contract_function_interaction.ts | 8 ------- .../end-to-end/src/e2e_static_calls.test.ts | 21 ++++++++----------- .../src/client/client_execution_context.ts | 10 +++++---- .../simulator/src/public/execution.ts | 2 +- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index 5fb523362f6..3b115f5940f 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -46,14 +46,6 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } } - /** - * Custom send supporting extended options - * @param options - An optional object containing additional configuration for the transaction. - * */ - public send(options: ExecutionRequestOptions = {}) { - return super.send(options); - } - /** * Custom simulate supporting extended options * @param options - An optional object containing additional configuration for the transaction. diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index e77d6938377..663f6527435 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -25,10 +25,7 @@ describe('e2e_static_calls', () => { describe('direct calls', () => { it('performs legal private static calls', async () => { - await childContract.methods - .privateGetValue(42n, wallet.getCompleteAddress().address) - .send({ static: true }) - .wait(); + await childContract.methods.privateGetValue(42n, wallet.getCompleteAddress().address).simulate({ static: true }); if (isGenerateTestDataEnabled()) { { @@ -61,18 +58,18 @@ describe('e2e_static_calls', () => { }, 100_000); it('performs legal public static calls', async () => { - await childContract.methods.pubGetValue(42n).send({ static: true }).wait(); + await childContract.methods.pubGetValue(42n).simulate({ static: true }); }, 100_000); it('fails when performing illegal private static calls', async () => { await expect( - childContract.methods.privateSetValue(42n, wallet.getCompleteAddress().address).send({ static: true }).wait(), - ).rejects.toThrow('Static call cannot create new notes'); + childContract.methods.privateSetValue(42n, wallet.getCompleteAddress().address).simulate({ static: true }), + ).rejects.toThrow('Static call cannot create new notes, emit L2->L1 messages or generate logs'); }, 100_000); it('fails when performing illegal public static calls', async () => { - await expect(childContract.methods.pubSetValue(42n).send({ static: true }).wait()).rejects.toThrow( - 'Static call cannot update the state', + await expect(childContract.methods.pubSetValue(42n).simulate({ static: true })).rejects.toThrow( + 'Static call cannot update state, emit L2->L1 messages or generate logs', ); }, 100_000); }); @@ -111,7 +108,7 @@ describe('e2e_static_calls', () => { ]) .send() .wait(), - ).rejects.toThrow('Static call cannot create new notes'); + ).rejects.toThrow('Static call cannot create new notes, emit L2->L1 messages or generate logs'); }, 100_000); it('fails when performing illegal public to public static calls', async () => { @@ -120,7 +117,7 @@ describe('e2e_static_calls', () => { .publicStaticCall(childContract.address, childContract.methods.pubSetValue.selector, [42n]) .send() .wait(), - ).rejects.toThrow('Static call cannot update the state'); + ).rejects.toThrow('Static call cannot update the state, emit L2->L1 messages or generate logs'); }, 100_000); it('fails when performing illegal enqueued public static calls', async () => { @@ -129,7 +126,7 @@ describe('e2e_static_calls', () => { .enqueueStaticCallToPubFunction(childContract.address, childContract.methods.pubSetValue.selector, [42n]) .send() .wait(), - ).rejects.toThrow('Static call cannot update the state'); + ).rejects.toThrow('Static call cannot update the state, emit L2->L1 messages or generate logs'); }, 100_000); }); }); diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index a50388ca2fb..9d31b74c141 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -306,11 +306,13 @@ export class ClientExecutionContext extends ViewDataOracle { #checkValidStaticCall(childExecutionResult: ExecutionResult) { if ( - childExecutionResult.newNotes.length > 0 || - childExecutionResult.encryptedLogs.logs.length == 0 || - childExecutionResult.unencryptedLogs.logs.length > 0 + childExecutionResult.callStackItem.publicInputs.newCommitments.length > 0 || + childExecutionResult.callStackItem.publicInputs.newNullifiers.length > 0 || + childExecutionResult.callStackItem.publicInputs.newL2ToL1Msgs.some(item => !item.isZero()) || + !childExecutionResult.callStackItem.publicInputs.encryptedLogPreimagesLength.isZero() || + !childExecutionResult.callStackItem.publicInputs.unencryptedLogPreimagesLength.isZero() ) { - throw new Error('Static call cannot create new notes or generate logs'); + throw new Error('Static call cannot create new notes, emit L2->L1 messages or generate logs'); } } diff --git a/yarn-project/simulator/src/public/execution.ts b/yarn-project/simulator/src/public/execution.ts index dbf298d637d..044b245f5e6 100644 --- a/yarn-project/simulator/src/public/execution.ts +++ b/yarn-project/simulator/src/public/execution.ts @@ -156,6 +156,6 @@ export function checkValidStaticCall( newL2ToL1Messages.length > 0 || unencryptedLogs.logs.length > 0 ) { - throw new Error('Static call cannot update the state'); + throw new Error('Static call cannot update the state, emit L2->L1 messages or generate logs'); } } From f093395d56c1940b611d34acb0a41368b9db0d87 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 12 Feb 2024 14:05:33 +0000 Subject: [PATCH 12/16] merge change --- yarn-project/simulator/src/public/public_execution_context.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index fd18d7622f1..6bd1193a4df 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -100,6 +100,7 @@ export class PublicExecutionContext extends TypedOracle { public emitUnencryptedLog(log: UnencryptedL2Log) { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/885) this.unencryptedLogs.push(log); + throw Error('tuhmuertoh'); this.log(`Emitted unencrypted log: "${log.toHumanReadable()}"`); } From e48e5eddec59562fa871d29ec512c36d7230beb0 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 13 Feb 2024 09:22:14 +0000 Subject: [PATCH 13/16] fixes --- .../end-to-end/src/e2e_static_calls.test.ts | 53 +------------------ .../crates/private-kernel-lib/src/common.nr | 2 +- .../src/client/client_execution_context.ts | 16 +++--- .../simulator/src/client/private_execution.ts | 3 ++ .../src/public/public_execution_context.ts | 1 - 5 files changed, 13 insertions(+), 62 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index 663f6527435..faa5eeca37c 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -23,59 +23,8 @@ describe('e2e_static_calls', () => { childContract = await ChildContract.deploy(wallet).send().deployed(); }, 100_000); - describe('direct calls', () => { - it('performs legal private static calls', async () => { - await childContract.methods.privateGetValue(42n, wallet.getCompleteAddress().address).simulate({ static: true }); - - if (isGenerateTestDataEnabled()) { - { - const privateKernelInputsInit = getTestData('private-kernel-inputs-init'); - const nestedCallPrivateKernelInput = privateKernelInputsInit[0]; - writeFileSync( - '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-init.hex', - nestedCallPrivateKernelInput.toBuffer().toString('hex'), - ); - } - - { - const privateKernelInputsInner = getTestData('private-kernel-inputs-inner'); - const nestedCallPrivateKernelInput = privateKernelInputsInner[privateKernelInputsInner.length - 1]; - writeFileSync( - '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-inner.hex', - nestedCallPrivateKernelInput.toBuffer().toString('hex'), - ); - } - - { - const privateKernelInputsOrdering = getTestData('private-kernel-inputs-ordering'); - const nestedCallPrivateKernelInput = privateKernelInputsOrdering[0]; - writeFileSync( - '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-ordering.hex', - nestedCallPrivateKernelInput.toBuffer().toString('hex'), - ); - } - } - }, 100_000); - - it('performs legal public static calls', async () => { - await childContract.methods.pubGetValue(42n).simulate({ static: true }); - }, 100_000); - - it('fails when performing illegal private static calls', async () => { - await expect( - childContract.methods.privateSetValue(42n, wallet.getCompleteAddress().address).simulate({ static: true }), - ).rejects.toThrow('Static call cannot create new notes, emit L2->L1 messages or generate logs'); - }, 100_000); - - it('fails when performing illegal public static calls', async () => { - await expect(childContract.methods.pubSetValue(42n).simulate({ static: true })).rejects.toThrow( - 'Static call cannot update state, emit L2->L1 messages or generate logs', - ); - }, 100_000); - }); - describe('parent calls child', () => { - it('performs legal private to private static calls', async () => { + it.only('performs legal private to private static calls', async () => { await parentContract.methods .privateStaticCall(childContract.address, childContract.methods.privateGetValue.selector, [ 42n, diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr index ad040a67b9c..ce595537045 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr @@ -126,7 +126,7 @@ fn perform_static_call_checks(private_call: PrivateCallData) { assert(new_l2_to_l1_msgs_length == 0, "new_l2_to_l1_msgs must be empty for static calls"); assert( - public_inputs.encrypted_log_preimages_length == 0, "No unencrypted logs are allowed for static calls" + public_inputs.encrypted_log_preimages_length == 0, "No encrypted logs are allowed for static calls" ); assert( diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 9d31b74c141..b8d3a992a0c 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -65,7 +65,7 @@ export class ClientExecutionContext extends ViewDataOracle { protected readonly contractAddress: AztecAddress, private readonly argsHash: Fr, private readonly txContext: TxContext, - private readonly callContext: CallContext, + public readonly callContext: CallContext, /** Header of a block whose state is used during private execution (not the block the transaction is included in). */ protected readonly historicalHeader: Header, /** List of transient auth witnesses to be used during this simulation */ @@ -304,15 +304,15 @@ export class ClientExecutionContext extends ViewDataOracle { this.log(`Emitted unencrypted log: "${text.length > 100 ? text.slice(0, 100) + '...' : text}"`); } - #checkValidStaticCall(childExecutionResult: ExecutionResult) { + #checkValidStaticCall(childExecutionResult: ExecutionResult, childName: string) { if ( - childExecutionResult.callStackItem.publicInputs.newCommitments.length > 0 || - childExecutionResult.callStackItem.publicInputs.newNullifiers.length > 0 || + childExecutionResult.callStackItem.publicInputs.newCommitments.some(item => !item.isEmpty()) || + childExecutionResult.callStackItem.publicInputs.newNullifiers.some(item => !item.isEmpty()) || childExecutionResult.callStackItem.publicInputs.newL2ToL1Msgs.some(item => !item.isZero()) || - !childExecutionResult.callStackItem.publicInputs.encryptedLogPreimagesLength.isZero() || - !childExecutionResult.callStackItem.publicInputs.unencryptedLogPreimagesLength.isZero() + !childExecutionResult.callStackItem.publicInputs.encryptedLogPreimagesLength.equals(new Fr(4)) || + !childExecutionResult.callStackItem.publicInputs.unencryptedLogPreimagesLength.equals(new Fr(4)) ) { - throw new Error('Static call cannot create new notes, emit L2->L1 messages or generate logs'); + throw new Error(`Static call to ${childName} cannot create new notes, emit L2->L1 messages or generate logs`); } } @@ -378,7 +378,7 @@ export class ClientExecutionContext extends ViewDataOracle { ); if (isStaticCall) { - this.#checkValidStaticCall(childExecutionResult); + this.#checkValidStaticCall(childExecutionResult, targetArtifact.name); } this.nestedExecutions.push(childExecutionResult); diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index cf2e579af08..dd769077403 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -24,6 +24,7 @@ export async function executePrivateFunction( ): Promise { const functionSelector = functionData.selector; log(`Executing external function ${contractAddress}:${functionSelector}`); + console.log(`Executing external function ${contractAddress}:${artifact.name}:${context.callContext.isStaticCall}`); const acir = Buffer.from(artifact.bytecode, 'base64'); const initialWitness = context.getInitialWitness(artifact); @@ -62,6 +63,8 @@ export async function executePrivateFunction( log(`Returning from call to ${contractAddress.toString()}:${functionSelector}`); + console.log(`returnting from call ${contractAddress}:${artifact.name}:${context.callContext.isStaticCall}`); + return { acir, partialWitness, diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index 6bd1193a4df..fd18d7622f1 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -100,7 +100,6 @@ export class PublicExecutionContext extends TypedOracle { public emitUnencryptedLog(log: UnencryptedL2Log) { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/885) this.unencryptedLogs.push(log); - throw Error('tuhmuertoh'); this.log(`Emitted unencrypted log: "${log.toHumanReadable()}"`); } From 0c4293c450a318d830c94aa8734fc497c8492e9a Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 13 Feb 2024 12:44:28 +0000 Subject: [PATCH 14/16] fixes and removed static in aztec.js --- .../aztec-nr/authwit/src/entrypoint/app.nr | 8 ++--- .../aztec-nr/authwit/src/entrypoint/fee.nr | 6 ++-- .../authwit/src/entrypoint/function_call.nr | 8 ++--- .../crates/private-kernel-lib/src/common.nr | 6 ++-- .../crates/public-kernel-lib/src/common.nr | 4 ++- .../src/defaults/account_entrypoint.ts | 6 ---- .../src/defaults/entrypoint_payload.ts | 6 ---- .../contract/contract_function_interaction.ts | 29 +++---------------- .../circuit-types/src/function_call.ts | 3 -- .../end-to-end/src/e2e_static_calls.test.ts | 7 ++--- yarn-project/noir-contracts.js/package.json | 2 +- .../noir-protocol-circuits-types/package.json | 2 +- .../noir-protocol-circuits-types/src/index.ts | 1 - yarn-project/package.json | 2 +- .../pxe/src/pxe_service/pxe_service.ts | 10 ++----- .../src/sequencer/public_processor.test.ts | 2 -- .../src/client/client_execution_context.ts | 6 ++-- .../simulator/src/client/private_execution.ts | 4 --- .../simulator/src/client/simulator.ts | 1 - .../client/unconstrained_execution.test.ts | 1 - 20 files changed, 31 insertions(+), 83 deletions(-) diff --git a/noir-projects/aztec-nr/authwit/src/entrypoint/app.nr b/noir-projects/aztec-nr/authwit/src/entrypoint/app.nr index b048fb5bd1a..9892b529830 100644 --- a/noir-projects/aztec-nr/authwit/src/entrypoint/app.nr +++ b/noir-projects/aztec-nr/authwit/src/entrypoint/app.nr @@ -4,9 +4,9 @@ use dep::aztec::protocol_types::{constants::GENERATOR_INDEX__SIGNATURE_PAYLOAD, use crate::entrypoint::function_call::{FunctionCall, FUNCTION_CALL_SIZE_IN_BYTES}; // FUNCTION_CALL_SIZE * ACCOUNT_MAX_CALLS + 1 -global APP_PAYLOAD_SIZE: Field = 21; +global APP_PAYLOAD_SIZE: Field = 17; // FUNCTION_CALL_SIZE_IN_BYTES * ACCOUNT_MAX_CALLS + 32 -global APP_PAYLOAD_SIZE_IN_BYTES: Field = 424; +global APP_PAYLOAD_SIZE_IN_BYTES: Field = 420; global ACCOUNT_MAX_CALLS: Field = 4; @@ -58,9 +58,9 @@ impl AppPayload { for call in self.function_calls { if !call.target_address.is_zero() { if call.is_public { - context.call_public_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, call.is_static); + context.call_public_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, false); } else { - let _result = context.call_private_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, call.is_static); + let _result = context.call_private_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, false); } } } diff --git a/noir-projects/aztec-nr/authwit/src/entrypoint/fee.nr b/noir-projects/aztec-nr/authwit/src/entrypoint/fee.nr index b617276f8dd..2c37fa36778 100644 --- a/noir-projects/aztec-nr/authwit/src/entrypoint/fee.nr +++ b/noir-projects/aztec-nr/authwit/src/entrypoint/fee.nr @@ -3,7 +3,7 @@ use dep::aztec::protocol_types::{constants::GENERATOR_INDEX__FEE_PAYLOAD, hash:: use crate::entrypoint::function_call::{FunctionCall}; // 2 * 4 (function call) + 1 -global FEE_PAYLOAD_SIZE: Field = 11; +global FEE_PAYLOAD_SIZE: Field = 9; // 2*97 + 32 global FEE_PAYLOAD_SIZE_IN_BYTES: Field = 226; @@ -54,9 +54,9 @@ impl FeePayload { for call in self.function_calls { if !call.target_address.is_zero() { if call.is_public { - context.call_public_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, call.is_static); + context.call_public_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, false); } else { - let _result = context.call_private_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, call.is_static); + let _result = context.call_private_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, false); } } } diff --git a/noir-projects/aztec-nr/authwit/src/entrypoint/function_call.nr b/noir-projects/aztec-nr/authwit/src/entrypoint/function_call.nr index cd1e4240e15..0b1f2ba0363 100644 --- a/noir-projects/aztec-nr/authwit/src/entrypoint/function_call.nr +++ b/noir-projects/aztec-nr/authwit/src/entrypoint/function_call.nr @@ -1,21 +1,20 @@ use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::Serialize}; // 1 (ARGS_HASH) + 1 (FUNCTION_SELECTOR) + 1 (TARGET_ADDRESS) + 1 (IS_PUBLIC) -global FUNCTION_CALL_SIZE: Field = 5; +global FUNCTION_CALL_SIZE: Field = 4; // 3 * 32 + 1 -global FUNCTION_CALL_SIZE_IN_BYTES: Field = 98; +global FUNCTION_CALL_SIZE_IN_BYTES: Field = 97; struct FunctionCall { args_hash: Field, function_selector: FunctionSelector, target_address: AztecAddress, is_public: bool, - is_static: bool } impl Serialize for FunctionCall { fn serialize(self) -> [Field; FUNCTION_CALL_SIZE] { - [self.args_hash, self.function_selector.to_field(), self.target_address.to_field(), self.is_public as Field, self.is_static as Field] + [self.args_hash, self.function_selector.to_field(), self.target_address.to_field(), self.is_public as Field] } } @@ -35,7 +34,6 @@ impl FunctionCall { bytes[i + 64] = target_address_bytes[i]; } bytes[96] = self.is_public as u8; - bytes[97] = self.is_static as u8; bytes } } diff --git a/noir-projects/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr index ce595537045..aa2fb147256 100644 --- a/noir-projects/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr @@ -125,12 +125,14 @@ fn perform_static_call_checks(private_call: PrivateCallData) { let new_l2_to_l1_msgs_length = array_length(public_inputs.new_l2_to_l1_msgs); assert(new_l2_to_l1_msgs_length == 0, "new_l2_to_l1_msgs must be empty for static calls"); + // TODO: reevaluate when implementing https://github.com/AztecProtocol/aztec-packages/issues/1165 + // This 4 magical number is the minimum size of the buffer, since it has to store the total length of all the serialized logs. assert( - public_inputs.encrypted_log_preimages_length == 0, "No encrypted logs are allowed for static calls" + public_inputs.encrypted_log_preimages_length == 4, "No encrypted logs are allowed for static calls" ); assert( - public_inputs.unencrypted_log_preimages_length == 0, "No unencrypted logs are allowed for static calls" + public_inputs.unencrypted_log_preimages_length == 4, "No unencrypted logs are allowed for static calls" ); } } diff --git a/noir-projects/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr index afd9792afbd..9931f4e01b4 100644 --- a/noir-projects/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr @@ -86,8 +86,10 @@ fn perform_static_call_checks(public_call: PublicCallData) { let new_l2_to_l1_msgs_length = array_length(public_inputs.new_l2_to_l1_msgs); assert(new_l2_to_l1_msgs_length == 0, "new_l2_to_l1_msgs must be empty for static calls"); + // TODO: reevaluate when implementing https://github.com/AztecProtocol/aztec-packages/issues/1165 + // This 4 magical number is the minimum size of the buffer, since it has to store the total length of all the serialized logs. assert( - public_inputs.unencrypted_log_preimages_length == 0, "No unencrypted logs are allowed for static calls" + public_inputs.unencrypted_log_preimages_length == 4, "No unencrypted logs are allowed for static calls" ); } } diff --git a/yarn-project/accounts/src/defaults/account_entrypoint.ts b/yarn-project/accounts/src/defaults/account_entrypoint.ts index dca981a8155..67b62181dfd 100644 --- a/yarn-project/accounts/src/defaults/account_entrypoint.ts +++ b/yarn-project/accounts/src/defaults/account_entrypoint.ts @@ -83,12 +83,6 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { }, }, { name: 'is_public', type: { kind: 'boolean' } }, - { - name: 'is_static', - type: { - kind: 'boolean', - }, - }, ], }, }, diff --git a/yarn-project/accounts/src/defaults/entrypoint_payload.ts b/yarn-project/accounts/src/defaults/entrypoint_payload.ts index 843dcd9b62f..5396c0b9374 100644 --- a/yarn-project/accounts/src/defaults/entrypoint_payload.ts +++ b/yarn-project/accounts/src/defaults/entrypoint_payload.ts @@ -24,9 +24,6 @@ type EntrypointFunctionCall = { // eslint-disable-next-line camelcase /** Whether the function is public or private */ is_public: boolean; - // eslint-disable-next-line camelcase - /** Whether the function is able to modify state */ - is_static: boolean; }; /** Encoded payload for the account contract entrypoint */ @@ -70,8 +67,6 @@ function buildPayload(calls: FunctionCall[], maxCalls: number): PayloadWithArgum target_address: call.to.toField(), // eslint-disable-next-line camelcase is_public: !call.functionData.isPrivate, - // eslint-disable-next-line camelcase - is_static: call.static, })); return { @@ -111,7 +106,6 @@ function flattenPayload(payload: EntrypointPayload) { call.function_selector, call.target_address, new Fr(call.is_public), - new Fr(call.is_static), ]), payload.nonce, ]; diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index 3b115f5940f..1e7a30d33d6 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -18,17 +18,6 @@ export type ViewMethodOptions = { from?: AztecAddress; }; -/** - * Options for sending a function execution request to a contract. - * Allows specifying whether it is a static call or not, and additional send method options. - */ -export type ExecutionRequestOptions = { - /** - * Wether the execution is static, i.e. it does not modify state - */ - static?: boolean; -} & SendMethodOptions; - /** * This is the class that is returned when calling e.g. `contract.methods.myMethod(arg0, arg1)`. * It contains available interactions one can call on a method, including view. @@ -46,26 +35,17 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } } - /** - * Custom simulate supporting extended options - * @param options - An optional object containing additional configuration for the transaction. - * */ - public simulate(options: ExecutionRequestOptions = {}) { - return super.simulate(options); - } - /** * Create a transaction execution request that represents this call, encoded and authenticated by the * user's wallet, ready to be simulated. - * @param options - An optional object containing additional configuration for the transaction. * @returns A Promise that resolves to a transaction instance. */ - public async create(options: ExecutionRequestOptions = {}): Promise { + public async create(): Promise { if (this.functionDao.functionType === FunctionType.UNCONSTRAINED) { throw new Error("Can't call `create` on an unconstrained function."); } if (!this.txRequest) { - this.txRequest = await this.wallet.createTxExecutionRequest([this.request(options)]); + this.txRequest = await this.wallet.createTxExecutionRequest([this.request()]); } return this.txRequest; } @@ -73,13 +53,12 @@ export class ContractFunctionInteraction extends BaseContractInteraction { /** * Returns an execution request that represents this operation. Useful as a building * block for constructing batch requests. - * @param options - An optional object containing additional configuration for the transaction. * @returns An execution request wrapped in promise. */ - public request(options: ExecutionRequestOptions = {}): FunctionCall { + public request(): FunctionCall { const args = encodeArguments(this.functionDao, this.args); const functionData = FunctionData.fromAbi(this.functionDao); - return { args, functionData, to: this.contractAddress, static: options.static ?? false }; + return { args, functionData, to: this.contractAddress }; } /** diff --git a/yarn-project/circuit-types/src/function_call.ts b/yarn-project/circuit-types/src/function_call.ts index e471201683f..c3d8e831ad4 100644 --- a/yarn-project/circuit-types/src/function_call.ts +++ b/yarn-project/circuit-types/src/function_call.ts @@ -8,8 +8,6 @@ export type FunctionCall = { functionData: FunctionData; /** The encoded args */ args: Fr[]; - /** Whether it's able to alter state or not */ - static: boolean; }; /** @@ -21,6 +19,5 @@ export function emptyFunctionCall() { to: AztecAddress.ZERO, functionData: FunctionData.empty(), args: [], - static: false, }; } diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index faa5eeca37c..1d635f51f8d 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -1,8 +1,5 @@ import { Wallet } from '@aztec/aztec.js'; -import { getTestData, isGenerateTestDataEnabled } from '@aztec/foundation/testing'; -import { ChildContract, ParentContract } from '@aztec/noir-contracts'; - -import { writeFileSync } from 'fs'; +import { ChildContract, ParentContract } from '@aztec/noir-contracts.js'; import { setup } from './fixtures/utils.js'; @@ -24,7 +21,7 @@ describe('e2e_static_calls', () => { }, 100_000); describe('parent calls child', () => { - it.only('performs legal private to private static calls', async () => { + it('performs legal private to private static calls', async () => { await parentContract.methods .privateStaticCall(childContract.address, childContract.methods.privateGetValue.selector, [ 42n, diff --git a/yarn-project/noir-contracts.js/package.json b/yarn-project/noir-contracts.js/package.json index df827fc8e0f..10195fd680b 100644 --- a/yarn-project/noir-contracts.js/package.json +++ b/yarn-project/noir-contracts.js/package.json @@ -49,4 +49,4 @@ "engines": { "node": ">=18" } -} \ No newline at end of file +} diff --git a/yarn-project/noir-protocol-circuits-types/package.json b/yarn-project/noir-protocol-circuits-types/package.json index eec6ca314b6..a580039ebca 100644 --- a/yarn-project/noir-protocol-circuits-types/package.json +++ b/yarn-project/noir-protocol-circuits-types/package.json @@ -57,4 +57,4 @@ "engines": { "node": ">=18" } -} \ No newline at end of file +} diff --git a/yarn-project/noir-protocol-circuits-types/src/index.ts b/yarn-project/noir-protocol-circuits-types/src/index.ts index 303bc90f52b..d544a9d3af9 100644 --- a/yarn-project/noir-protocol-circuits-types/src/index.ts +++ b/yarn-project/noir-protocol-circuits-types/src/index.ts @@ -121,7 +121,6 @@ export async function executeInner( const params: InnerInputType = { input: mapPrivateKernelInnerCircuitPrivateInputsToNoir(privateKernelInnerCircuitPrivateInputs), }; - const returnType = await executePrivateKernelInnerWithACVM(params); return mapPrivateKernelInnerCircuitPublicInputsFromNoir(returnType); diff --git a/yarn-project/package.json b/yarn-project/package.json index c83a0292d4b..06f7739b380 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -72,4 +72,4 @@ "@noir-lang/noir_wasm": "portal:../noir/packages/noir_wasm", "@noir-lang/noir_js": "portal:../noir/packages/noir_js" } -} \ No newline at end of file +} diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 6c37826b806..f08954f0131 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -424,7 +424,7 @@ export class PXEService implements PXE { // all simulations must be serialized w.r.t. the synchronizer return await this.jobQueue.put(async () => { // TODO - Should check if `from` has the permission to call the view function. - const functionCall = await this.#getFunctionCall(functionName, args, to, false); + const functionCall = await this.#getFunctionCall(functionName, args, to); const executionResult = await this.#simulateUnconstrained(functionCall); // TODO - Return typed result based on the function artifact. @@ -487,12 +487,7 @@ export class PXEService implements PXE { return this.node.getUnencryptedLogs(filter); } - async #getFunctionCall( - functionName: string, - args: any[], - to: AztecAddress, - isStatic: boolean, - ): Promise { + async #getFunctionCall(functionName: string, args: any[], to: AztecAddress): Promise { const contract = await this.db.getContract(to); if (!contract) { throw new Error( @@ -509,7 +504,6 @@ export class PXEService implements PXE { args: encodeArguments(functionDao, args), functionData: FunctionData.fromAbi(functionDao), to, - static: isStatic, }; } diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts index 043170ab35b..d5de3308699 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -216,7 +216,6 @@ describe('public_processor', () => { to: makeAztecAddress(30), functionData: new FunctionData(makeSelector(5), false, false, false), args: new Array(ARGS_LENGTH).fill(Fr.ZERO), - static: false, }), ]; publicExecutor.simulate.mockResolvedValue(publicExecutionResult); @@ -258,7 +257,6 @@ describe('public_processor', () => { to: makeAztecAddress(30), functionData: new FunctionData(makeSelector(5), false, false, false), args: new Array(ARGS_LENGTH).fill(Fr.ZERO), - static: false, }), ]; publicExecutor.simulate.mockRejectedValueOnce(new SimulationError('Simulation Failed', [])); diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 742418e1c4f..b8b95341f88 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -307,7 +307,7 @@ export class ClientExecutionContext extends ViewDataOracle { this.log(`Emitted unencrypted log: "${text.length > 100 ? text.slice(0, 100) + '...' : text}"`); } - #checkValidStaticCall(childExecutionResult: ExecutionResult, childName: string) { + #checkValidStaticCall(childExecutionResult: ExecutionResult) { if ( childExecutionResult.callStackItem.publicInputs.newCommitments.some(item => !item.isEmpty()) || childExecutionResult.callStackItem.publicInputs.newNullifiers.some(item => !item.isEmpty()) || @@ -315,7 +315,7 @@ export class ClientExecutionContext extends ViewDataOracle { !childExecutionResult.callStackItem.publicInputs.encryptedLogPreimagesLength.equals(new Fr(4)) || !childExecutionResult.callStackItem.publicInputs.unencryptedLogPreimagesLength.equals(new Fr(4)) ) { - throw new Error(`Static call to ${childName} cannot create new notes, emit L2->L1 messages or generate logs`); + throw new Error(`Static call cannot create new notes, emit L2->L1 messages or generate logs`); } } @@ -381,7 +381,7 @@ export class ClientExecutionContext extends ViewDataOracle { ); if (isStaticCall) { - this.#checkValidStaticCall(childExecutionResult, targetArtifact.name); + this.#checkValidStaticCall(childExecutionResult); } this.nestedExecutions.push(childExecutionResult); diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index dd769077403..bb6198e3ad1 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -24,8 +24,6 @@ export async function executePrivateFunction( ): Promise { const functionSelector = functionData.selector; log(`Executing external function ${contractAddress}:${functionSelector}`); - console.log(`Executing external function ${contractAddress}:${artifact.name}:${context.callContext.isStaticCall}`); - const acir = Buffer.from(artifact.bytecode, 'base64'); const initialWitness = context.getInitialWitness(artifact); const acvmCallback = new Oracle(context); @@ -63,8 +61,6 @@ export async function executePrivateFunction( log(`Returning from call to ${contractAddress.toString()}:${functionSelector}`); - console.log(`returnting from call ${contractAddress}:${artifact.name}:${context.callContext.isStaticCall}`); - return { acir, partialWitness, diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index 12aca92f0f8..57cfda70177 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -201,7 +201,6 @@ export class AcirSimulator { to: contractAddress, functionData: FunctionData.empty(), args: encodeArguments(artifact, [contractAddress, nonce, storageSlot, noteTypeId, extendedNoteItems]), - static: false, }; const [innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier] = (await this.runUnconstrained( diff --git a/yarn-project/simulator/src/client/unconstrained_execution.test.ts b/yarn-project/simulator/src/client/unconstrained_execution.test.ts index 6dd33ce6c77..384d7a07472 100644 --- a/yarn-project/simulator/src/client/unconstrained_execution.test.ts +++ b/yarn-project/simulator/src/client/unconstrained_execution.test.ts @@ -65,7 +65,6 @@ describe('Unconstrained Execution test suite', () => { to: contractAddress, functionData: new FunctionData(FunctionSelector.empty(), false, true, true), args: encodeArguments(artifact, [owner]), - static: false, }; const result = await acirSimulator.runUnconstrained(execRequest, artifact, AztecAddress.random()); From c65473d4955c072a9e34e8b6156833ffef561a0d Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 14 Feb 2024 09:28:08 +0000 Subject: [PATCH 15/16] fix --- yarn-project/simulator/src/client/client_execution_context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index b8b95341f88..5f98af4e655 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -65,7 +65,7 @@ export class ClientExecutionContext extends ViewDataOracle { protected readonly contractAddress: AztecAddress, private readonly argsHash: Fr, private readonly txContext: TxContext, - public readonly callContext: CallContext, + private readonly callContext: CallContext, /** Header of a block whose state is used during private execution (not the block the transaction is included in). */ protected readonly historicalHeader: Header, /** List of transient auth witnesses to be used during this simulation */ From 0191d2ccbe330ad76cfd78e3aceb1b4ebbc43c03 Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 14 Feb 2024 13:11:57 +0000 Subject: [PATCH 16/16] fix and format --- .../aztec-nr/authwit/src/entrypoint/app.nr | 14 ++++++++++++-- .../aztec-nr/authwit/src/entrypoint/fee.nr | 14 ++++++++++++-- .../aztec-nr/aztec/src/context/private_context.nr | 12 ++++++------ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/noir-projects/aztec-nr/authwit/src/entrypoint/app.nr b/noir-projects/aztec-nr/authwit/src/entrypoint/app.nr index 9892b529830..71936bc06c6 100644 --- a/noir-projects/aztec-nr/authwit/src/entrypoint/app.nr +++ b/noir-projects/aztec-nr/authwit/src/entrypoint/app.nr @@ -58,9 +58,19 @@ impl AppPayload { for call in self.function_calls { if !call.target_address.is_zero() { if call.is_public { - context.call_public_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, false); + context.call_public_function_with_packed_args( + call.target_address, + call.function_selector, + call.args_hash, + false + ); } else { - let _result = context.call_private_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, false); + let _result = context.call_private_function_with_packed_args( + call.target_address, + call.function_selector, + call.args_hash, + false + ); } } } diff --git a/noir-projects/aztec-nr/authwit/src/entrypoint/fee.nr b/noir-projects/aztec-nr/authwit/src/entrypoint/fee.nr index 2c37fa36778..20b07243841 100644 --- a/noir-projects/aztec-nr/authwit/src/entrypoint/fee.nr +++ b/noir-projects/aztec-nr/authwit/src/entrypoint/fee.nr @@ -54,9 +54,19 @@ impl FeePayload { for call in self.function_calls { if !call.target_address.is_zero() { if call.is_public { - context.call_public_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, false); + context.call_public_function_with_packed_args( + call.target_address, + call.function_selector, + call.args_hash, + false + ); } else { - let _result = context.call_private_function_with_packed_args(call.target_address, call.function_selector, call.args_hash, false); + let _result = context.call_private_function_with_packed_args( + call.target_address, + call.function_selector, + call.args_hash, + false + ); } } } diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 4e9eb239690..d7f63f030a2 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -274,17 +274,17 @@ impl PrivateContext { pub fn call_private_function_no_args_static( &mut self, contract_address: AztecAddress, - function_selector: FunctionSelector, + function_selector: FunctionSelector ) -> [Field; RETURN_VALUES_LENGTH] { self.call_private_function_with_packed_args(contract_address, function_selector, 0, true) } - pub fn call_private_function_with_packed_args( + fn call_private_function_with_packed_args( &mut self, contract_address: AztecAddress, function_selector: FunctionSelector, args_hash: Field, - is_static_call: bool, + is_static_call: bool ) -> [Field; RETURN_VALUES_LENGTH] { let item = call_private_function_internal( contract_address, @@ -349,10 +349,10 @@ impl PrivateContext { self.call_public_function_with_packed_args(contract_address, function_selector, 0, false) } - pub fn call_public_function_no_args_static( + pub fn call_public_function_no_args_static( &mut self, contract_address: AztecAddress, - function_selector: FunctionSelector, + function_selector: FunctionSelector ) { self.call_public_function_with_packed_args(contract_address, function_selector, 0, true) } @@ -362,7 +362,7 @@ impl PrivateContext { contract_address: AztecAddress, function_selector: FunctionSelector, args_hash: Field, - is_static_call: bool, + is_static_call: bool ) { let fields = enqueue_public_function_call_internal( contract_address,