diff --git a/yarn-project/aztec/src/local-network/local-network.ts b/yarn-project/aztec/src/local-network/local-network.ts index 4f62ea214738..5856a93bf8e7 100644 --- a/yarn-project/aztec/src/local-network/local-network.ts +++ b/yarn-project/aztec/src/local-network/local-network.ts @@ -16,15 +16,11 @@ import { SecretValue } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; import type { LogFn } from '@aztec/foundation/log'; import { DateProvider, TestDateProvider } from '@aztec/foundation/timer'; -import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; import { protocolContractsHash } from '@aztec/protocol-contracts'; import { SequencerState } from '@aztec/sequencer-client'; -import { FunctionSelector, countArgumentsSize } from '@aztec/stdlib/abi'; -import type { FunctionAbi } from '@aztec/stdlib/abi'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { getContractClassFromArtifact } from '@aztec/stdlib/contract'; -import type { AllowedElement, ProvingJobBroker } from '@aztec/stdlib/interfaces/server'; +import type { ProvingJobBroker } from '@aztec/stdlib/interfaces/server'; import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees'; import { type TelemetryClient, @@ -43,43 +39,12 @@ import { createAccountLogs } from '../cli/util.js'; import { DefaultMnemonic } from '../mnemonic.js'; import { AnvilTestWatcher } from '../testing/anvil_test_watcher.js'; import { EpochTestSettler } from '../testing/epoch_test_settler.js'; +import { getTokenAllowedSetupFunctions } from '../testing/token_allowed_setup.js'; import { getBananaFPCAddress, setupBananaFPC } from './banana_fpc.js'; import { getSponsoredFPCAddress } from './sponsored_fpc.js'; const logger = createLogger('local-network'); -/** - * Returns Token-specific allowlist entries for FPC-based fee payments. - * The local network deploys a banana FPC and Token contracts, so the node must allow Token setup functions. - */ -async function getTokenAllowedSetupFunctions(): Promise { - const tokenClassId = (await getContractClassFromArtifact(TokenContractArtifact)).id; - const allFunctions: FunctionAbi[] = (TokenContractArtifact.functions as FunctionAbi[]).concat( - TokenContractArtifact.nonDispatchPublicFunctions || [], - ); - const getCalldataLength = (name: string) => { - const fn = allFunctions.find(f => f.name === name)!; - return 1 + countArgumentsSize(fn); - }; - const increaseBalanceSelector = await FunctionSelector.fromSignature('_increase_public_balance((Field),u128)'); - const transferInPublicSelector = await FunctionSelector.fromSignature( - 'transfer_in_public((Field),(Field),u128,Field)', - ); - return [ - { - classId: tokenClassId, - selector: increaseBalanceSelector, - calldataLength: getCalldataLength('_increase_public_balance'), - onlySelf: true, - }, - { - classId: tokenClassId, - selector: transferInPublicSelector, - calldataLength: getCalldataLength('transfer_in_public'), - }, - ]; -} - const localAnvil = foundry; /** diff --git a/yarn-project/aztec/src/testing/index.ts b/yarn-project/aztec/src/testing/index.ts index eaf2f836c9a3..97d6da751638 100644 --- a/yarn-project/aztec/src/testing/index.ts +++ b/yarn-project/aztec/src/testing/index.ts @@ -2,3 +2,4 @@ export { AnvilTestWatcher } from './anvil_test_watcher.js'; export { EthCheatCodes, RollupCheatCodes } from '@aztec/ethereum/test'; export { CheatCodes } from './cheat_codes.js'; export { EpochTestSettler } from './epoch_test_settler.js'; +export { getTokenAllowedSetupFunctions } from './token_allowed_setup.js'; diff --git a/yarn-project/aztec/src/testing/token_allowed_setup.ts b/yarn-project/aztec/src/testing/token_allowed_setup.ts new file mode 100644 index 000000000000..539b9521bddf --- /dev/null +++ b/yarn-project/aztec/src/testing/token_allowed_setup.ts @@ -0,0 +1,19 @@ +import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; +import { buildAllowedElement } from '@aztec/p2p/msg_validators'; +import { getContractClassFromArtifact } from '@aztec/stdlib/contract'; +import type { AllowedElement } from '@aztec/stdlib/interfaces/server'; + +/** + * Returns Token-specific allowlist entries needed for FPC-based fee payments. + * These are test-only: FPC-based fee payment with custom tokens won't work on mainnet alpha. + */ +export async function getTokenAllowedSetupFunctions(): Promise { + const tokenClassId = (await getContractClassFromArtifact(TokenContractArtifact)).id; + const target = { classId: tokenClassId }; + return Promise.all([ + // Token: needed for private transfers via FPC (transfer_to_public enqueues this) + buildAllowedElement(TokenContractArtifact, target, '_increase_public_balance', { onlySelf: true }), + // Token: needed for public transfers via FPC (fee_entrypoint_public enqueues this) + buildAllowedElement(TokenContractArtifact, target, 'transfer_in_public'), + ]); +} diff --git a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts index 26e6e78f3dc1..c461800a7912 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts @@ -4,7 +4,7 @@ import { type FeePaymentMethod, PrivateFeePaymentMethod, SponsoredFeePaymentMeth import { type Logger, createLogger } from '@aztec/aztec.js/log'; import type { AztecNode } from '@aztec/aztec.js/node'; import type { Wallet } from '@aztec/aztec.js/wallet'; -import { CheatCodes } from '@aztec/aztec/testing'; +import { CheatCodes, getTokenAllowedSetupFunctions } from '@aztec/aztec/testing'; import { createExtendedL1Client } from '@aztec/ethereum/client'; import { RollupContract } from '@aztec/ethereum/contracts'; import type { DeployAztecL1ContractsArgs } from '@aztec/ethereum/deploy-aztec-l1-contracts'; @@ -19,16 +19,12 @@ import { AMMContract } from '@aztec/noir-contracts.js/AMM'; import { FPCContract } from '@aztec/noir-contracts.js/FPC'; import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; import { SponsoredFPCContract } from '@aztec/noir-contracts.js/SponsoredFPC'; -import { TokenContract as BananaCoin, TokenContract, TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; +import { TokenContract as BananaCoin, TokenContract } from '@aztec/noir-contracts.js/Token'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice'; import { type PXEConfig, getPXEConfig } from '@aztec/pxe/server'; -import { FunctionSelector, countArgumentsSize } from '@aztec/stdlib/abi'; -import type { FunctionAbi } from '@aztec/stdlib/abi'; -import { getContractClassFromArtifact } from '@aztec/stdlib/contract'; import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; import { GasSettings } from '@aztec/stdlib/gas'; -import type { AllowedElement } from '@aztec/stdlib/interfaces/server'; import { deriveSigningKey } from '@aztec/stdlib/keys'; import { MNEMONIC } from '../../fixtures/fixtures.js'; @@ -46,35 +42,6 @@ import { type ClientFlowsConfig, FULL_FLOWS_CONFIG, KEY_FLOWS_CONFIG } from './c const { BENCHMARK_CONFIG } = process.env; -/** Returns Token-specific allowlist entries for FPC-based fee payments (test-only). */ -async function getTokenAllowedSetupFunctions(): Promise { - const tokenClassId = (await getContractClassFromArtifact(TokenContractArtifact)).id; - const allFunctions: FunctionAbi[] = (TokenContractArtifact.functions as FunctionAbi[]).concat( - TokenContractArtifact.nonDispatchPublicFunctions || [], - ); - const getCalldataLength = (name: string) => { - const fn = allFunctions.find(f => f.name === name)!; - return 1 + countArgumentsSize(fn); - }; - const increaseBalanceSelector = await FunctionSelector.fromSignature('_increase_public_balance((Field),u128)'); - const transferInPublicSelector = await FunctionSelector.fromSignature( - 'transfer_in_public((Field),(Field),u128,Field)', - ); - return [ - { - classId: tokenClassId, - selector: increaseBalanceSelector, - calldataLength: getCalldataLength('_increase_public_balance'), - onlySelf: true, - }, - { - classId: tokenClassId, - selector: transferInPublicSelector, - calldataLength: getCalldataLength('transfer_in_public'), - }, - ]; -} - export type AccountType = 'ecdsar1' | 'schnorr'; export type FeePaymentMethodGetter = (wallet: Wallet, sender: AztecAddress) => Promise; export type BenchmarkingFeePaymentMethod = 'bridged_fee_juice' | 'private_fpc' | 'sponsored_fpc' | 'fee_juice'; diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index f163d566681d..f83d7a125f39 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -1,7 +1,7 @@ import type { AztecAddress } from '@aztec/aztec.js/addresses'; import { type Logger, createLogger } from '@aztec/aztec.js/log'; import type { AztecNode } from '@aztec/aztec.js/node'; -import { CheatCodes } from '@aztec/aztec/testing'; +import { CheatCodes, getTokenAllowedSetupFunctions } from '@aztec/aztec/testing'; import { createExtendedL1Client } from '@aztec/ethereum/client'; import { RollupContract } from '@aztec/ethereum/contracts'; import type { DeployAztecL1ContractsArgs } from '@aztec/ethereum/deploy-aztec-l1-contracts'; @@ -14,16 +14,12 @@ import { AppSubscriptionContract } from '@aztec/noir-contracts.js/AppSubscriptio import { FPCContract } from '@aztec/noir-contracts.js/FPC'; import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; import { SponsoredFPCContract } from '@aztec/noir-contracts.js/SponsoredFPC'; -import { TokenContract as BananaCoin, TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; +import { TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { CounterContract } from '@aztec/noir-test-contracts.js/Counter'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice'; -import { FunctionSelector, countArgumentsSize } from '@aztec/stdlib/abi'; -import type { FunctionAbi } from '@aztec/stdlib/abi'; -import { getContractClassFromArtifact } from '@aztec/stdlib/contract'; import { GasSettings } from '@aztec/stdlib/gas'; import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; -import type { AllowedElement } from '@aztec/stdlib/interfaces/server'; import { getContract } from 'viem'; @@ -41,46 +37,6 @@ import { type BalancesFn, getBalancesFn, setupSponsoredFPC } from '../fixtures/u import { FeeJuicePortalTestingHarnessFactory, type GasBridgingTestHarness } from '../shared/gas_portal_test_harness.js'; import { TestWallet } from '../test-wallet/test_wallet.js'; -/** Returns the calldata length for a function: 1 (selector) + arguments size. */ -function getCalldataLength(functionName: string): number { - const allFunctions: FunctionAbi[] = (TokenContractArtifact.functions as FunctionAbi[]).concat( - TokenContractArtifact.nonDispatchPublicFunctions || [], - ); - const fn = allFunctions.find(f => f.name === functionName); - if (!fn) { - throw new Error(`Unknown function ${functionName} in Token artifact`); - } - return 1 + countArgumentsSize(fn); -} - -/** - * Returns Token-specific allowlist entries needed for FPC-based fee payments. - * These are test-only — FPC-based fee payment with custom tokens won't work on mainnet alpha. - */ -async function getTokenAllowedSetupFunctions(): Promise { - const tokenClassId = (await getContractClassFromArtifact(TokenContractArtifact)).id; - const increaseBalanceSelector = await FunctionSelector.fromSignature('_increase_public_balance((Field),u128)'); - const transferInPublicSelector = await FunctionSelector.fromSignature( - 'transfer_in_public((Field),(Field),u128,Field)', - ); - - return [ - // Token: needed for private transfers via FPC (transfer_to_public enqueues this) - { - classId: tokenClassId, - selector: increaseBalanceSelector, - calldataLength: getCalldataLength('_increase_public_balance'), - onlySelf: true, - }, - // Token: needed for public transfers via FPC (fee_entrypoint_public enqueues this) - { - classId: tokenClassId, - selector: transferInPublicSelector, - calldataLength: getCalldataLength('transfer_in_public'), - }, - ]; -} - /** * Test fixture for testing fees. Provides the following setup steps: * InitialAccounts: Initializes 3 Schnorr account contracts. diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/allowed_public_setup.ts b/yarn-project/p2p/src/msg_validators/tx_validator/allowed_public_setup.ts index b8709732a192..41231ae207b2 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/allowed_public_setup.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/allowed_public_setup.ts @@ -1,55 +1,30 @@ import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { AuthRegistryArtifact } from '@aztec/protocol-contracts/auth-registry'; import { FeeJuiceArtifact } from '@aztec/protocol-contracts/fee-juice'; -import { FunctionSelector, countArgumentsSize } from '@aztec/stdlib/abi'; -import type { ContractArtifact, FunctionAbi } from '@aztec/stdlib/abi'; import type { AllowedElement } from '@aztec/stdlib/interfaces/server'; -/** Returns the expected calldata length for a function: 1 (selector) + arguments size. */ -function getCalldataLength(artifact: ContractArtifact, functionName: string): number { - const allFunctions: FunctionAbi[] = (artifact.functions as FunctionAbi[]).concat( - artifact.nonDispatchPublicFunctions || [], - ); - const fn = allFunctions.find(f => f.name === functionName); - if (!fn) { - throw new Error(`Unknown function ${functionName} in artifact ${artifact.name}`); - } - return 1 + countArgumentsSize(fn); -} +import { buildAllowedElement } from './allowed_setup_helpers.js'; let defaultAllowedSetupFunctions: AllowedElement[] | undefined; /** Returns the default list of functions allowed to run in the setup phase of a transaction. */ export async function getDefaultAllowedSetupFunctions(): Promise { if (defaultAllowedSetupFunctions === undefined) { - const setAuthorizedInternalSelector = await FunctionSelector.fromSignature('_set_authorized((Field),Field,bool)'); - const setAuthorizedSelector = await FunctionSelector.fromSignature('set_authorized(Field,bool)'); - const increaseBalanceSelector = await FunctionSelector.fromSignature('_increase_public_balance((Field),u128)'); - - defaultAllowedSetupFunctions = [ + defaultAllowedSetupFunctions = await Promise.all([ // AuthRegistry: needed for authwit support via private path (set_authorized_private enqueues _set_authorized) - { - address: ProtocolContractAddress.AuthRegistry, - selector: setAuthorizedInternalSelector, - calldataLength: getCalldataLength(AuthRegistryArtifact, '_set_authorized'), + buildAllowedElement(AuthRegistryArtifact, { address: ProtocolContractAddress.AuthRegistry }, '_set_authorized', { onlySelf: true, rejectNullMsgSender: true, - }, + }), // AuthRegistry: needed for authwit support via public path (PublicFeePaymentMethod calls set_authorized directly) - { - address: ProtocolContractAddress.AuthRegistry, - selector: setAuthorizedSelector, - calldataLength: getCalldataLength(AuthRegistryArtifact, 'set_authorized'), + buildAllowedElement(AuthRegistryArtifact, { address: ProtocolContractAddress.AuthRegistry }, 'set_authorized', { rejectNullMsgSender: true, - }, + }), // FeeJuice: needed for claiming on the same tx as a spend (claim_and_end_setup enqueues this) - { - address: ProtocolContractAddress.FeeJuice, - selector: increaseBalanceSelector, - calldataLength: getCalldataLength(FeeJuiceArtifact, '_increase_public_balance'), + buildAllowedElement(FeeJuiceArtifact, { address: ProtocolContractAddress.FeeJuice }, '_increase_public_balance', { onlySelf: true, - }, - ]; + }), + ]); } return defaultAllowedSetupFunctions; } diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/allowed_setup_helpers.ts b/yarn-project/p2p/src/msg_validators/tx_validator/allowed_setup_helpers.ts new file mode 100644 index 000000000000..3e63283c4696 --- /dev/null +++ b/yarn-project/p2p/src/msg_validators/tx_validator/allowed_setup_helpers.ts @@ -0,0 +1,31 @@ +import type { Fr } from '@aztec/foundation/curves/bn254'; +import { FunctionSelector, countArgumentsSize, getAllFunctionAbis } from '@aztec/stdlib/abi'; +import type { ContractArtifact } from '@aztec/stdlib/abi'; +import type { AztecAddress } from '@aztec/stdlib/aztec-address'; +import type { AllowedElement } from '@aztec/stdlib/interfaces/server'; + +/** + * Builds an AllowedElement from a contract artifact, deriving both the function selector + * and calldata length from the artifact instead of hardcoding signature strings. + */ +export async function buildAllowedElement( + artifact: ContractArtifact, + target: { address: AztecAddress } | { classId: Fr }, + functionName: string, + opts?: { onlySelf?: boolean; rejectNullMsgSender?: boolean }, +): Promise { + const allFunctions = getAllFunctionAbis(artifact); + const fn = allFunctions.find(f => f.name === functionName); + if (!fn) { + throw new Error(`Unknown function ${functionName} in artifact ${artifact.name}`); + } + const selector = await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters); + const calldataLength = 1 + countArgumentsSize(fn); + return { + ...target, + selector, + calldataLength, + ...(opts?.onlySelf ? { onlySelf: true } : {}), + ...(opts?.rejectNullMsgSender ? { rejectNullMsgSender: true } : {}), + } as AllowedElement; +} diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/fee_payer_balance.ts b/yarn-project/p2p/src/msg_validators/tx_validator/fee_payer_balance.ts index 4ce80e9cae29..af3936effa58 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/fee_payer_balance.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/fee_payer_balance.ts @@ -1,5 +1,6 @@ +import { FeeJuiceArtifact } from '@aztec/protocol-contracts/fee-juice'; import { getCallRequestsWithCalldataByPhase } from '@aztec/simulator/server'; -import { FunctionSelector } from '@aztec/stdlib/abi'; +import { FunctionSelector, getAllFunctionAbis } from '@aztec/stdlib/abi'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { type Tx, TxExecutionPhase } from '@aztec/stdlib/tx'; @@ -8,7 +9,10 @@ export type FeePayerBalanceDelta = { claimAmount: bigint; }; -const increasePublicBalanceSelectorPromise = FunctionSelector.fromSignature('_increase_public_balance((Field),u128)'); +const increasePublicBalanceSelectorPromise = (() => { + const fn = getAllFunctionAbis(FeeJuiceArtifact).find(f => f.name === '_increase_public_balance')!; + return FunctionSelector.fromNameAndParameters(fn.name, fn.parameters); +})(); export function getTxFeeLimit(tx: Tx): bigint { return tx.data.constants.txContext.gasSettings.getFeeLimit().toBigInt(); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/index.ts b/yarn-project/p2p/src/msg_validators/tx_validator/index.ts index dcdd4fb050e9..893796772a3b 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/index.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/index.ts @@ -8,6 +8,7 @@ export * from './gas_validator.js'; export * from './phases_validator.js'; export * from './test_utils.js'; export * from './allowed_public_setup.js'; +export * from './allowed_setup_helpers.js'; export * from './archive_cache.js'; export * from './tx_permitted_validator.js'; export * from './timestamp_validator.js';