Skip to content

Commit

Permalink
feat: sequencer validates setup/teardown function selectors (#5649)
Browse files Browse the repository at this point in the history
This PR refactors the `TxValidator` class so it's more flexible and adds
whitelisting capabilities on the function selector for setup/teardown
phases.

Fix #5401
  • Loading branch information
alexghr authored Apr 10, 2024
1 parent 4c3f3fd commit 8f8ad56
Show file tree
Hide file tree
Showing 32 changed files with 952 additions and 691 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ contract AppSubscription {
note.remaining_txs -= 1;
storage.subscriptions.at(user_address).replace(&mut note, true);

context.call_public_function(
storage.gas_token_address.read_private(),
FunctionSelector::from_signature("check_balance(Field)"),
// max gas limit in Gas tokens
[42]
);

context.call_public_function(
storage.gas_token_address.read_private(),
FunctionSelector::from_signature("pay_fee(Field)"),
Expand Down
25 changes: 20 additions & 5 deletions yarn-project/circuit-types/src/interfaces/configs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
import { type AztecAddress, type EthAddress, type Fr } from '@aztec/circuits.js';
import { type AztecAddress, type EthAddress, type Fr, type FunctionSelector } from '@aztec/circuits.js';

/** A function that the sequencer allows to run in either setup or teardown phase */
export type AllowedFunction =
| {
/** The contract address this selector is valid for */
address: AztecAddress;
/** The function selector */
selector: FunctionSelector;
}
| {
/** The contract class this selector is valid for */
classId: Fr;
/** The function selector */
selector: FunctionSelector;
};

/**
* The sequencer configuration.
Expand All @@ -19,9 +34,9 @@ export interface SequencerConfig {
/** The path to the ACVM binary */
acvmBinaryPath?: string;

/** The list of permitted fee payment contract classes */
allowedFeePaymentContractClasses?: Fr[];
/** The list of functions calls allowed to run in setup */
allowedFunctionsInSetup?: AllowedFunction[];

/** The list of permitted fee payment contract instances. Takes precedence over contract classes */
allowedFeePaymentContractInstances?: AztecAddress[];
/** The list of functions calls allowed to run teardown */
allowedFunctionsInTeardown?: AllowedFunction[];
}
10 changes: 5 additions & 5 deletions yarn-project/circuit-types/src/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ export const mockTx = (

data.forPublic.endNonRevertibleData.newNullifiers[0] = firstNullifier;

data.forPublic.endNonRevertibleData.publicCallStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, i =>
i < numberOfNonRevertiblePublicCallRequests ? publicCallRequests[i].toCallRequest() : CallRequest.empty(),
data.forPublic.end.publicCallStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, i =>
i < numberOfRevertiblePublicCallRequests ? publicCallRequests[i].toCallRequest() : CallRequest.empty(),
);

data.forPublic.end.publicCallStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, i =>
i < numberOfRevertiblePublicCallRequests
? publicCallRequests[i + numberOfNonRevertiblePublicCallRequests].toCallRequest()
data.forPublic.endNonRevertibleData.publicCallStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, i =>
i < numberOfNonRevertiblePublicCallRequests
? publicCallRequests[i + numberOfRevertiblePublicCallRequests].toCallRequest()
: CallRequest.empty(),
);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
PrivateFeePaymentMethod,
PublicFeePaymentMethod,
TxStatus,
getContractClassFromArtifact,
} from '@aztec/aztec.js';
import { FPCContract, GasTokenContract, TokenContract } from '@aztec/noir-contracts.js';
import { getCanonicalGasTokenAddress } from '@aztec/protocol-contracts/gas-token';
Expand Down Expand Up @@ -39,7 +38,6 @@ describe('benchmarks/tx_size_fees', () => {

await aztecNode.setConfig({
feeRecipient: sequencerAddress,
allowedFeePaymentContractClasses: [getContractClassFromArtifact(FPCContract.artifact).id],
});

await publicDeployAccounts(aliceWallet, wallets);
Expand Down
3 changes: 1 addition & 2 deletions yarn-project/end-to-end/src/e2e_account_init_fees.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
computeMessageSecretHash,
generatePublicKey,
} from '@aztec/aztec.js';
import { type AztecAddress, CompleteAddress, Fq, getContractClassFromArtifact } from '@aztec/circuits.js';
import { type AztecAddress, CompleteAddress, Fq } from '@aztec/circuits.js';
import {
TokenContract as BananaCoin,
FPCContract,
Expand Down Expand Up @@ -89,7 +89,6 @@ describe('e2e_fees_account_init', () => {
sequencersAddress = sequencer.getAddress();

await ctx.aztecNode.setConfig({
allowedFeePaymentContractClasses: [getContractClassFromArtifact(FPCContract.artifact).id],
feeRecipient: sequencersAddress,
});

Expand Down
5 changes: 0 additions & 5 deletions yarn-project/end-to-end/src/e2e_dapp_subscription.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
PrivateFeePaymentMethod,
PublicFeePaymentMethod,
SentTx,
getContractClassFromArtifact,
} from '@aztec/aztec.js';
import { DefaultDappEntrypoint } from '@aztec/entrypoints/dapp';
import {
Expand Down Expand Up @@ -70,10 +69,6 @@ describe('e2e_dapp_subscription', () => {

await publicDeployAccounts(wallets[0], wallets);

await aztecNode.setConfig({
allowedFeePaymentContractClasses: [getContractClassFromArtifact(FPCContract.artifact).id],
});

// this should be a SignerlessWallet but that can't call public functions directly
gasTokenContract = await GasTokenContract.at(
getCanonicalGasTokenAddress(deployL1ContractsValues.l1ContractAddresses.gasPortalAddress),
Expand Down
6 changes: 1 addition & 5 deletions yarn-project/end-to-end/src/e2e_fees.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
computeAuthWitMessageHash,
computeMessageSecretHash,
} from '@aztec/aztec.js';
import { FunctionData, getContractClassFromArtifact } from '@aztec/circuits.js';
import { FunctionData } from '@aztec/circuits.js';
import { type ContractArtifact, decodeFunctionSignature } from '@aztec/foundation/abi';
import {
TokenContract as BananaCoin,
Expand Down Expand Up @@ -57,10 +57,6 @@ describe('e2e_fees', () => {
const { wallets: _wallets, aztecNode, deployL1ContractsValues, logger, pxe } = await setup(3);
wallets = _wallets;

await aztecNode.setConfig({
allowedFeePaymentContractClasses: [getContractClassFromArtifact(FPCContract.artifact).id],
});

logFunctionSignatures(BananaCoin.artifact, logger);
logFunctionSignatures(FPCContract.artifact, logger);
logFunctionSignatures(GasTokenContract.artifact, logger);
Expand Down
1 change: 1 addition & 0 deletions yarn-project/sequencer-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@aztec/foundation": "workspace:^",
"@aztec/l1-artifacts": "workspace:^",
"@aztec/merkle-tree": "workspace:^",
"@aztec/noir-contracts.js": "workspace:^",
"@aztec/noir-protocol-circuits-types": "workspace:^",
"@aztec/p2p": "workspace:^",
"@aztec/protocol-contracts": "workspace:^",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { getGlobalVariableBuilder } from '../global_variable_builder/index.js';
import { getL1Publisher } from '../publisher/index.js';
import { Sequencer, type SequencerConfig } from '../sequencer/index.js';
import { PublicProcessorFactory } from '../sequencer/public_processor.js';
import { TxValidatorFactory } from '../sequencer/tx_validator_factory.js';
import { TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';

/**
* Encapsulates the full sequencer and publisher.
Expand Down
100 changes: 93 additions & 7 deletions yarn-project/sequencer-client/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { AztecAddress, Fr } from '@aztec/circuits.js';
import { type AllowedFunction } from '@aztec/circuit-types';
import { AztecAddress, Fr, FunctionSelector, getContractClassFromArtifact } from '@aztec/circuits.js';
import { type L1ContractAddresses, NULL_KEY } from '@aztec/ethereum';
import { EthAddress } from '@aztec/foundation/eth-address';
import { EcdsaAccountContractArtifact } from '@aztec/noir-contracts.js/EcdsaAccount';
import { FPCContract } from '@aztec/noir-contracts.js/FPC';
import { GasTokenContract } from '@aztec/noir-contracts.js/GasToken';
import { SchnorrAccountContractArtifact } from '@aztec/noir-contracts.js/SchnorrAccount';
import { SchnorrHardcodedAccountContractArtifact } from '@aztec/noir-contracts.js/SchnorrHardcodedAccount';
import { SchnorrSingleKeyAccountContractArtifact } from '@aztec/noir-contracts.js/SchnorrSingleKeyAccount';
import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token';

import { type Hex } from 'viem';

Expand Down Expand Up @@ -40,8 +48,8 @@ export function getConfigEnvVars(): SequencerClientConfig {
SEQ_TX_POLLING_INTERVAL_MS,
SEQ_MAX_TX_PER_BLOCK,
SEQ_MIN_TX_PER_BLOCK,
SEQ_FPC_CLASSES,
SEQ_FPC_INSTANCES,
SEQ_ALLOWED_SETUP_FN,
SEQ_ALLOWED_TEARDOWN_FN,
AVAILABILITY_ORACLE_CONTRACT_ADDRESS,
ROLLUP_CONTRACT_ADDRESS,
REGISTRY_CONTRACT_ADDRESS,
Expand Down Expand Up @@ -90,9 +98,87 @@ export function getConfigEnvVars(): SequencerClientConfig {
feeRecipient: FEE_RECIPIENT ? AztecAddress.fromString(FEE_RECIPIENT) : undefined,
acvmWorkingDirectory: ACVM_WORKING_DIRECTORY ? ACVM_WORKING_DIRECTORY : undefined,
acvmBinaryPath: ACVM_BINARY_PATH ? ACVM_BINARY_PATH : undefined,
allowedFeePaymentContractClasses: SEQ_FPC_CLASSES ? SEQ_FPC_CLASSES.split(',').map(Fr.fromString) : [],
allowedFeePaymentContractInstances: SEQ_FPC_INSTANCES
? SEQ_FPC_INSTANCES.split(',').map(AztecAddress.fromString)
: [],
allowedFunctionsInSetup: SEQ_ALLOWED_SETUP_FN
? parseSequencerAllowList(SEQ_ALLOWED_SETUP_FN)
: getDefaultAllowedSetupFunctions(),
allowedFunctionsInTeardown: SEQ_ALLOWED_TEARDOWN_FN
? parseSequencerAllowList(SEQ_ALLOWED_TEARDOWN_FN)
: getDefaultAllowedTeardownFunctions(),
};
}

function parseSequencerAllowList(value: string): AllowedFunction[] {
const entries: AllowedFunction[] = [];

if (!value) {
return entries;
}

for (const val of value.split(',')) {
const [identifierString, selectorString] = val.split(':');
const selector = FunctionSelector.fromString(selectorString);

if (identifierString.startsWith('0x')) {
entries.push({
address: AztecAddress.fromString(identifierString),
selector,
});
} else {
entries.push({
classId: Fr.fromString(identifierString),
selector,
});
}
}

return entries;
}

function getDefaultAllowedSetupFunctions(): AllowedFunction[] {
return [
{
classId: getContractClassFromArtifact(SchnorrAccountContractArtifact).id,
selector: FunctionSelector.fromSignature('approve_public_authwit(Field)'),
},
{
classId: getContractClassFromArtifact(SchnorrHardcodedAccountContractArtifact).id,
selector: FunctionSelector.fromSignature('approve_public_authwit(Field)'),
},
{
classId: getContractClassFromArtifact(SchnorrSingleKeyAccountContractArtifact).id,
selector: FunctionSelector.fromSignature('approve_public_authwit(Field)'),
},
{
classId: getContractClassFromArtifact(EcdsaAccountContractArtifact).id,
selector: FunctionSelector.fromSignature('approve_public_authwit(Field)'),
},

// needed for private transfers via FPC
{
classId: getContractClassFromArtifact(TokenContractArtifact).id,
selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
},

{
classId: getContractClassFromArtifact(FPCContract.artifact).id,
selector: FunctionSelector.fromSignature('prepare_fee((Field),Field,(Field),Field)'),
},
];
}

function getDefaultAllowedTeardownFunctions(): AllowedFunction[] {
return [
{
classId: getContractClassFromArtifact(GasTokenContract.artifact).id,
selector: FunctionSelector.fromSignature('pay_fee(Field)'),
},
{
classId: getContractClassFromArtifact(FPCContract.artifact).id,
selector: FunctionSelector.fromSignature('pay_fee((Field),Field,(Field))'),
},
{
classId: getContractClassFromArtifact(FPCContract.artifact).id,
selector: FunctionSelector.fromSignature('pay_fee_with_shielded_rebate(Field,(Field),Field)'),
},
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,18 @@ export abstract class AbstractPhaseManager {
publicInputs: PrivateKernelTailCircuitPublicInputs,
enqueuedPublicFunctionCalls: PublicCallRequest[],
): Record<PublicKernelPhase, PublicCallRequest[]> {
const data = publicInputs.forPublic;
if (!data) {
return {
[PublicKernelPhase.SETUP]: [],
[PublicKernelPhase.APP_LOGIC]: [],
[PublicKernelPhase.TEARDOWN]: [],
[PublicKernelPhase.TAIL]: [],
};
}
const publicCallsStack = enqueuedPublicFunctionCalls.slice().reverse();
const nonRevertibleCallStack = publicInputs.forPublic!.endNonRevertibleData.publicCallStack.filter(
i => !i.isEmpty(),
);
const revertibleCallStack = publicInputs.forPublic!.end.publicCallStack.filter(i => !i.isEmpty());
const nonRevertibleCallStack = data.endNonRevertibleData.publicCallStack.filter(i => !i.isEmpty());
const revertibleCallStack = data.end.publicCallStack.filter(i => !i.isEmpty());

const callRequestsStack = publicCallsStack
.map(call => call.toCallRequest())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ import { type MockProxy, mock } from 'jest-mock-extended';
import { type PublicKernelCircuitSimulator } from '../simulator/index.js';
import { type ContractsDataSourcePublicDB, type WorldStatePublicDB } from '../simulator/public_executor.js';
import { RealPublicKernelCircuitSimulator } from '../simulator/public_kernel.js';
import { type TxValidator } from '../tx_validator/tx_validator.js';
import { PublicProcessor } from './public_processor.js';
import { type TxValidator } from './tx_validator.js';

describe('public_processor', () => {
let db: MockProxy<MerkleTreeOperations>;
Expand Down Expand Up @@ -276,7 +276,7 @@ describe('public_processor', () => {
throw new Error(`Unexpected execution request: ${execution}`);
});

const txValidator: MockProxy<TxValidator> = mock<TxValidator>();
const txValidator: MockProxy<TxValidator<ProcessedTx>> = mock();
txValidator.validateTxs.mockRejectedValue([[], [tx]]);

const [processed, failed] = await processor.process([tx], 1, prover, txValidator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import { type MerkleTreeOperations } from '@aztec/world-state';
import { type PublicKernelCircuitSimulator } from '../simulator/index.js';
import { ContractsDataSourcePublicDB, WorldStateDB, WorldStatePublicDB } from '../simulator/public_executor.js';
import { RealPublicKernelCircuitSimulator } from '../simulator/public_kernel.js';
import { type TxValidator } from '../tx_validator/tx_validator.js';
import { type AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
import { PhaseManagerFactory } from './phase_manager_factory.js';
import { type TxValidator } from './tx_validator.js';

/**
* Creates new instances of PublicProcessor given the provided merkle tree db and contract data source.
Expand Down Expand Up @@ -90,7 +90,7 @@ export class PublicProcessor {
txs: Tx[],
maxTransactions = txs.length,
blockProver?: BlockProver,
txValidator?: TxValidator,
txValidator?: TxValidator<ProcessedTx>,
): Promise<[ProcessedTx[], FailedTx[], ProcessReturnValues[]]> {
// The processor modifies the tx objects in place, so we need to clone them.
txs = txs.map(tx => Tx.clone(tx));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ import { type MockProxy, mock, mockFn } from 'jest-mock-extended';

import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
import { type L1Publisher } from '../index.js';
import { TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
import { type PublicProcessor, type PublicProcessorFactory } from './public_processor.js';
import { Sequencer } from './sequencer.js';
import { TxValidatorFactory } from './tx_validator_factory.js';

describe('sequencer', () => {
let publisher: MockProxy<L1Publisher>;
Expand Down Expand Up @@ -109,9 +109,6 @@ describe('sequencer', () => {
l1ToL2MessageSource,
publicProcessorFactory,
new TxValidatorFactory(merkleTreeOps, contractSource, EthAddress.random()),
{
allowedFeePaymentContractClasses: [fpcClassId],
},
);
});

Expand Down
Loading

0 comments on commit 8f8ad56

Please sign in to comment.