diff --git a/noir-projects/aztec-nr/aztec/src/authwit/account.nr b/noir-projects/aztec-nr/aztec/src/authwit/account.nr index ba8cec3d4fc7..aa07575d888a 100644 --- a/noir-projects/aztec-nr/aztec/src/authwit/account.nr +++ b/noir-projects/aztec-nr/aztec/src/authwit/account.nr @@ -56,7 +56,13 @@ impl AccountActions<&mut PrivateContext> { pub fn entrypoint(self, app_payload: AppPayload, fee_payment_method: u8, cancellable: bool) { let valid_fn = self.is_valid_impl; - assert(valid_fn(self.context, app_payload.hash())); + let message_hash = compute_authwit_message_hash( + self.context.this_address(), + self.context.chain_id(), + self.context.version(), + app_payload.hash(), + ); + assert(valid_fn(self.context, message_hash)); if fee_payment_method == AccountFeePaymentMethodOptions.PREEXISTING_FEE_JUICE { self.context.set_as_fee_payer(); diff --git a/yarn-project/aztec.js/src/account/account.ts b/yarn-project/aztec.js/src/account/account.ts index 622a383e7ab8..1b7c72feecd8 100644 --- a/yarn-project/aztec.js/src/account/account.ts +++ b/yarn-project/aztec.js/src/account/account.ts @@ -53,8 +53,8 @@ export class BaseAccount implements Account { return this.entrypoint.createTxExecutionRequest(exec, gasSettings, chainInfo, options); } - wrapExecutionPayload(exec: ExecutionPayload, options?: any): Promise { - return this.entrypoint.wrapExecutionPayload(exec, options); + wrapExecutionPayload(exec: ExecutionPayload, chainInfo: ChainInfo, options?: any): Promise { + return this.entrypoint.wrapExecutionPayload(exec, chainInfo, options); } async createAuthWit(messageHashOrIntent: CallIntent | IntentInnerHash, chainInfo: ChainInfo): Promise { diff --git a/yarn-project/aztec.js/src/account/account_with_secret_key.ts b/yarn-project/aztec.js/src/account/account_with_secret_key.ts index 3c54e372333a..7bc50132bdec 100644 --- a/yarn-project/aztec.js/src/account/account_with_secret_key.ts +++ b/yarn-project/aztec.js/src/account/account_with_secret_key.ts @@ -32,8 +32,8 @@ export class AccountWithSecretKey implements Account { return this.account.createTxExecutionRequest(exec, gasSettings, chainInfo, options); } - wrapExecutionPayload(exec: ExecutionPayload, options?: any): Promise { - return this.account.wrapExecutionPayload(exec, options); + wrapExecutionPayload(exec: ExecutionPayload, chainInfo: ChainInfo, options?: any): Promise { + return this.account.wrapExecutionPayload(exec, chainInfo, options); } createAuthWit(intent: IntentInnerHash | CallIntent, chainInfo: ChainInfo): Promise { return this.account.createAuthWit(intent, chainInfo); diff --git a/yarn-project/aztec.js/src/account/signerless_account.ts b/yarn-project/aztec.js/src/account/signerless_account.ts index c6c2e7b7e861..6b1962dc6690 100644 --- a/yarn-project/aztec.js/src/account/signerless_account.ts +++ b/yarn-project/aztec.js/src/account/signerless_account.ts @@ -28,8 +28,8 @@ export class SignerlessAccount implements Account { return this.entrypoint.createTxExecutionRequest(exec, gasSettings, chainInfo); } - wrapExecutionPayload(exec: ExecutionPayload, options?: any): Promise { - return this.entrypoint.wrapExecutionPayload(exec, options); + wrapExecutionPayload(exec: ExecutionPayload, chainInfo: ChainInfo, options?: any): Promise { + return this.entrypoint.wrapExecutionPayload(exec, chainInfo, options); } createAuthWit(_intent: Fr | Buffer | IntentInnerHash | CallIntent): Promise { diff --git a/yarn-project/aztec.js/src/wallet/account_entrypoint_meta_payment_method.ts b/yarn-project/aztec.js/src/wallet/account_entrypoint_meta_payment_method.ts index 8e73d560e1c9..1bb1ebba4ea2 100644 --- a/yarn-project/aztec.js/src/wallet/account_entrypoint_meta_payment_method.ts +++ b/yarn-project/aztec.js/src/wallet/account_entrypoint_meta_payment_method.ts @@ -1,4 +1,5 @@ import { AccountFeePaymentMethodOptions } from '@aztec/entrypoints/account'; +import type { ChainInfo } from '@aztec/entrypoints/interfaces'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { GasSettings } from '@aztec/stdlib/gas'; @@ -25,6 +26,7 @@ import type { FeePaymentMethod } from '../fee/fee_payment_method.js'; export class AccountEntrypointMetaPaymentMethod implements FeePaymentMethod { constructor( private account: Account, + private chainInfo: ChainInfo, private paymentMethod?: FeePaymentMethod, private feeEntrypointOptions?: any, ) {} @@ -61,7 +63,7 @@ export class AccountEntrypointMetaPaymentMethod implements FeePaymentMethod { } // Use the generic wrapping mechanism from the account interface - return this.account.wrapExecutionPayload(innerPayload, options); + return this.account.wrapExecutionPayload(innerPayload, this.chainInfo, options); } getFeePayer(): Promise { diff --git a/yarn-project/aztec.js/src/wallet/deploy_account_method.ts b/yarn-project/aztec.js/src/wallet/deploy_account_method.ts index a6f6954db431..06fa52e36884 100644 --- a/yarn-project/aztec.js/src/wallet/deploy_account_method.ts +++ b/yarn-project/aztec.js/src/wallet/deploy_account_method.ts @@ -100,11 +100,12 @@ export class DeployAccountMethod exte * @param feeEntrypointOptions - Optional entrypoint-specific options for wrapping. If not provided, will be auto-computed based on the payment method. * @returns A FeePaymentMethod that routes the original one through the account's entrypoint (AccountEntrypointMetaPaymentMethod) */ - private getSelfFeePaymentMethod(originalPaymentMethod?: FeePaymentMethod, feeEntrypointOptions?: any) { + private async getSelfFeePaymentMethod(originalPaymentMethod?: FeePaymentMethod, feeEntrypointOptions?: any) { if (!this.address) { throw new Error('Instance is not yet constructed. This is a bug!'); } - return new AccountEntrypointMetaPaymentMethod(this.account, originalPaymentMethod, feeEntrypointOptions); + const chainInfo = await this.wallet.getChainInfo(); + return new AccountEntrypointMetaPaymentMethod(this.account, chainInfo, originalPaymentMethod, feeEntrypointOptions); } /** @@ -128,7 +129,10 @@ export class DeployAccountMethod exte const executionPayloads = [deploymentExecutionPayload]; // If this is a self-deployment, manage the fee accordingly if (opts?.deployer?.equals(AztecAddress.ZERO)) { - const feePaymentMethod = this.getSelfFeePaymentMethod(opts?.fee?.paymentMethod, opts?.fee?.feeEntrypointOptions); + const feePaymentMethod = await this.getSelfFeePaymentMethod( + opts?.fee?.paymentMethod, + opts?.fee?.feeEntrypointOptions, + ); const feeExecutionPayload = await feePaymentMethod.getExecutionPayload(); // Notice they are reversed (fee payment usually goes first): // this is because we need to construct the contract BEFORE it can pay for its own fee diff --git a/yarn-project/entrypoints/src/account_entrypoint.ts b/yarn-project/entrypoints/src/account_entrypoint.ts index 114910fa342a..b6bc73535fce 100644 --- a/yarn-project/entrypoints/src/account_entrypoint.ts +++ b/yarn-project/entrypoints/src/account_entrypoint.ts @@ -1,5 +1,6 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import { type FunctionAbi, FunctionCall, FunctionSelector, encodeArguments } from '@aztec/stdlib/abi'; +import { computeOuterAuthWitHash } from '@aztec/stdlib/auth-witness'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { GasSettings } from '@aztec/stdlib/gas'; import { ExecutionPayload, HashedValues, TxContext, TxExecutionRequest } from '@aztec/stdlib/tx'; @@ -66,7 +67,7 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { options: DefaultAccountEntrypointOptions, ): Promise { const { authWitnesses, capsules, extraHashedArgs } = exec; - const callData = await this.#buildEntrypointCallData(exec, options); + const callData = await this.#buildEntrypointCallData(exec, chainInfo, options); const entrypointHashedArgs = await HashedValues.fromArgs(callData.encodedArgs); const txRequest = TxExecutionRequest.from({ firstCallArgsHash: entrypointHashedArgs.hash, @@ -84,10 +85,11 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { async wrapExecutionPayload( exec: ExecutionPayload, + chainInfo: ChainInfo, options: DefaultAccountEntrypointOptions, ): Promise { const { authWitnesses, capsules, extraHashedArgs, feePayer } = exec; - const callData = await this.#buildEntrypointCallData(exec, options); + const callData = await this.#buildEntrypointCallData(exec, chainInfo, options); // Build the entrypoint function call const entrypointCall = FunctionCall.from({ @@ -114,10 +116,15 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { * Builds the shared data needed for both creating a tx execution request and wrapping an execution payload. * This includes encoding calls, building entrypoint arguments, and creating the authwitness. * @param exec - The execution payload containing calls to encode + * @param chainInfo - Chain information (chainId and version) for replay protection * @param options - Account entrypoint options including tx nonce and fee payment method * @returns Encoded call data, ABI, function selector, and auth witness */ - async #buildEntrypointCallData(exec: ExecutionPayload, options: DefaultAccountEntrypointOptions) { + async #buildEntrypointCallData( + exec: ExecutionPayload, + chainInfo: ChainInfo, + options: DefaultAccountEntrypointOptions, + ) { const { calls } = exec; const { cancellable, txNonce, feePaymentMethodOptions } = options; @@ -129,7 +136,9 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { const functionSelector = await FunctionSelector.fromNameAndParameters(abi.name, abi.parameters); - const payloadAuthWitness = await this.auth.createAuthWit(await encodedCalls.hash()); + const payloadHash = await encodedCalls.hash(); + const messageHash = await computeOuterAuthWitHash(this.address, chainInfo.chainId, chainInfo.version, payloadHash); + const payloadAuthWitness = await this.auth.createAuthWit(messageHash); return { encodedCalls, diff --git a/yarn-project/entrypoints/src/default_entrypoint.ts b/yarn-project/entrypoints/src/default_entrypoint.ts index 0b5274908bee..f7bed850c1ea 100644 --- a/yarn-project/entrypoints/src/default_entrypoint.ts +++ b/yarn-project/entrypoints/src/default_entrypoint.ts @@ -41,7 +41,7 @@ export class DefaultEntrypoint implements EntrypointInterface { ); } - async wrapExecutionPayload(exec: ExecutionPayload, _options?: any): Promise { + async wrapExecutionPayload(exec: ExecutionPayload, _chainInfo: ChainInfo, _options?: any): Promise { if (exec.calls.length !== 1) { throw new Error(`DefaultEntrypoint can only wrap a single call, got ${exec.calls.length}`); } diff --git a/yarn-project/entrypoints/src/default_multi_call_entrypoint.ts b/yarn-project/entrypoints/src/default_multi_call_entrypoint.ts index 60c905a415b0..9eef30e7565b 100644 --- a/yarn-project/entrypoints/src/default_multi_call_entrypoint.ts +++ b/yarn-project/entrypoints/src/default_multi_call_entrypoint.ts @@ -37,7 +37,7 @@ export class DefaultMultiCallEntrypoint implements EntrypointInterface { return Promise.resolve(txRequest); } - async wrapExecutionPayload(exec: ExecutionPayload, _options?: any): Promise { + async wrapExecutionPayload(exec: ExecutionPayload, _chainInfo: ChainInfo, _options?: any): Promise { const { authWitnesses, capsules, extraHashedArgs } = exec; const callData = await this.#buildEntrypointCallData(exec); const entrypointCall = FunctionCall.from({ diff --git a/yarn-project/entrypoints/src/interfaces.ts b/yarn-project/entrypoints/src/interfaces.ts index 182b5c0bbffb..ebdd30610a1d 100644 --- a/yarn-project/entrypoints/src/interfaces.ts +++ b/yarn-project/entrypoints/src/interfaces.ts @@ -51,11 +51,12 @@ export interface EntrypointInterface { * of a single entrypoint call. * * @param exec - The execution payload to wrap + * @param chainInfo - Chain information (chainId and version) for replay protection * @param options - Implementation-specific options * @returns A new execution payload with a single call to this entrypoint * @throws Error if the payload cannot be wrapped (e.g., exceeds call limit) */ - wrapExecutionPayload(exec: ExecutionPayload, options?: any): Promise; + wrapExecutionPayload(exec: ExecutionPayload, chainInfo: ChainInfo, options?: any): Promise; } /** Creates authorization witnesses. */