Skip to content

Commit

Permalink
feat: tx_validator checks fn selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
alexghr committed Apr 10, 2024
1 parent 57a1d69 commit 83e80aa
Show file tree
Hide file tree
Showing 31 changed files with 952 additions and 684 deletions.
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 83e80aa

Please sign in to comment.