diff --git a/.test_patterns.yml b/.test_patterns.yml index 098f71697288..506b15f4fe28 100644 --- a/.test_patterns.yml +++ b/.test_patterns.yml @@ -104,6 +104,10 @@ tests: error_regex: "TimeoutError: Timeout awaiting isMined" owners: - *palla + - regex: "simple e2e_p2p/gossip_network" + error_regex: "TimeoutError: Timeout awaiting isMined" + owners: + - *palla - regex: "simple e2e_fees/private_payments" owners: - *phil diff --git a/yarn-project/archiver/src/test/mock_l2_block_source.ts b/yarn-project/archiver/src/test/mock_l2_block_source.ts index 3f954b58aaa2..a2bcb65fdb9e 100644 --- a/yarn-project/archiver/src/test/mock_l2_block_source.ts +++ b/yarn-project/archiver/src/test/mock_l2_block_source.ts @@ -1,15 +1,19 @@ import { DefaultL1ContractsConfig } from '@aztec/ethereum'; import { Buffer32 } from '@aztec/foundation/buffer'; import { EthAddress } from '@aztec/foundation/eth-address'; +import type { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; +import type { FunctionSelector } from '@aztec/stdlib/abi'; +import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { L2Block, L2BlockHash, type L2BlockSource, type L2Tips } from '@aztec/stdlib/block'; +import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract'; import { type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers'; import { type BlockHeader, TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx'; /** * A mocked implementation of L2BlockSource to be used in tests. */ -export class MockL2BlockSource implements L2BlockSource { +export class MockL2BlockSource implements L2BlockSource, ContractDataSource { protected l2Blocks: L2Block[] = []; private provenBlockNumber: number = 0; @@ -221,4 +225,28 @@ export class MockL2BlockSource implements L2BlockSource { this.log.verbose('Stopping mock L2 block source'); return Promise.resolve(); } + + getContractClass(_id: Fr): Promise { + return Promise.resolve(undefined); + } + + getBytecodeCommitment(_id: Fr): Promise { + return Promise.resolve(undefined); + } + + getContract(_address: AztecAddress, _blockNumber?: number): Promise { + return Promise.resolve(undefined); + } + + getContractClassIds(): Promise { + return Promise.resolve([]); + } + + getDebugFunctionName(_address: AztecAddress, _selector: FunctionSelector): Promise { + return Promise.resolve(undefined); + } + + registerContractFunctionSignatures(_address: AztecAddress, _signatures: string[]): Promise { + return Promise.resolve(); + } } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index be053acd29cb..73d576dc15c1 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -23,7 +23,7 @@ import { openTmpStore } from '@aztec/kv-store/lmdb'; import { RollupAbi } from '@aztec/l1-artifacts'; import { SHA256Trunc, StandardTree, UnbalancedTree } from '@aztec/merkle-tree'; import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions'; -import { type P2P, createP2PClient } from '@aztec/p2p'; +import { type P2P, createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { GlobalVariableBuilder, @@ -31,7 +31,6 @@ import { type SequencerPublisher, createSlasherClient, createValidatorForAcceptingTxs, - getDefaultAllowedSetupFunctions, } from '@aztec/sequencer-client'; import { PublicProcessorFactory } from '@aztec/simulator/server'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; @@ -989,7 +988,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { blockNumber, l1ChainId: this.l1ChainId, rollupVersion: this.version, - setupAllowList: this.config.allowedInSetup ?? (await getDefaultAllowedSetupFunctions()), + setupAllowList: this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions()), gasFees: await this.getCurrentBaseFees(), skipFeeEnforcement, }); diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 4c73fa2aafd5..868f9fa9fcad 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -148,7 +148,6 @@ export type EnvVar = | 'ROLLUP_CONTRACT_ADDRESS' | 'SENTINEL_ENABLED' | 'SENTINEL_HISTORY_LENGTH_IN_EPOCHS' - | 'SEQ_ALLOWED_SETUP_FN' | 'SEQ_MAX_BLOCK_SIZE_IN_BYTES' | 'SEQ_MAX_TX_PER_BLOCK' | 'SEQ_MIN_TX_PER_BLOCK' @@ -169,6 +168,7 @@ export type EnvVar = | 'TEST_ACCOUNTS' | 'SPONSORED_FPC' | 'TX_GOSSIP_VERSION' + | 'TX_PUBLIC_SETUP_ALLOWLIST' | 'TXE_PORT' | 'VALIDATOR_ATTESTATIONS_POLLING_INTERVAL_MS' | 'VALIDATOR_DISABLED' diff --git a/yarn-project/p2p/package.json b/yarn-project/p2p/package.json index e25fd653cd31..57776c68f256 100644 --- a/yarn-project/p2p/package.json +++ b/yarn-project/p2p/package.json @@ -69,8 +69,10 @@ "@aztec/epoch-cache": "workspace:^", "@aztec/foundation": "workspace:^", "@aztec/kv-store": "workspace:^", + "@aztec/noir-contracts.js": "workspace:^", "@aztec/noir-protocol-circuits-types": "workspace:^", "@aztec/protocol-contracts": "workspace:^", + "@aztec/simulator": "workspace:^", "@aztec/stdlib": "workspace:^", "@aztec/telemetry-client": "workspace:^", "@chainsafe/discv5": "9.0.0", diff --git a/yarn-project/p2p/src/client/factory.ts b/yarn-project/p2p/src/client/factory.ts index cfbffd945bdb..bf1ba3bd40d5 100644 --- a/yarn-project/p2p/src/client/factory.ts +++ b/yarn-project/p2p/src/client/factory.ts @@ -4,6 +4,7 @@ import type { AztecAsyncKVStore } from '@aztec/kv-store'; import type { DataStoreConfig } from '@aztec/kv-store/config'; import { createStore } from '@aztec/kv-store/lmdb-v2'; import type { L2BlockSource } from '@aztec/stdlib/block'; +import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { ClientProtocolCircuitVerifier, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; import { P2PClientType } from '@aztec/stdlib/p2p'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; @@ -29,7 +30,7 @@ type P2PClientDeps = { export const createP2PClient = async ( clientType: T, _config: P2PConfig & DataStoreConfig, - l2BlockSource: L2BlockSource, + archiver: L2BlockSource & ContractDataSource, proofVerifier: ClientProtocolCircuitVerifier, worldStateSynchronizer: WorldStateSynchronizer, epochCache: EpochCacheInterface, @@ -73,7 +74,7 @@ export const createP2PClient = async ( discoveryService, peerId, mempools, - l2BlockSource, + archiver, epochCache, proofVerifier, worldStateSynchronizer, @@ -85,5 +86,5 @@ export const createP2PClient = async ( logger.verbose('P2P is disabled. Using dummy P2P service'); p2pService = new DummyP2PService(); } - return new P2PClient(clientType, store, l2BlockSource, mempools, p2pService, config, telemetry); + return new P2PClient(clientType, store, archiver, mempools, p2pService, config, telemetry); }; diff --git a/yarn-project/p2p/src/client/p2p_client.ts b/yarn-project/p2p/src/client/p2p_client.ts index d1b0747cccb1..6074e6cca71c 100644 --- a/yarn-project/p2p/src/client/p2p_client.ts +++ b/yarn-project/p2p/src/client/p2p_client.ts @@ -9,6 +9,7 @@ import type { L2Tips, PublishedL2Block, } from '@aztec/stdlib/block'; +import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { P2PApi, PeerInfo, ProverCoordination } from '@aztec/stdlib/interfaces/server'; import { BlockAttestation, type BlockProposal, ConsensusPayload, type P2PClientType } from '@aztec/stdlib/p2p'; import type { Tx, TxHash } from '@aztec/stdlib/tx'; @@ -211,7 +212,7 @@ export class P2PClient constructor( _clientType: T, store: AztecAsyncKVStore, - private l2BlockSource: L2BlockSource, + private l2BlockSource: L2BlockSource & ContractDataSource, mempools: MemPools, private p2pService: P2PService, config: Partial = {}, diff --git a/yarn-project/sequencer-client/src/config.test.ts b/yarn-project/p2p/src/config.test.ts similarity index 80% rename from yarn-project/sequencer-client/src/config.test.ts rename to yarn-project/p2p/src/config.test.ts index 77bf867dda67..4b84aed21d1e 100644 --- a/yarn-project/sequencer-client/src/config.test.ts +++ b/yarn-project/p2p/src/config.test.ts @@ -2,10 +2,10 @@ import { Fr } from '@aztec/foundation/fields'; import { FunctionSelector } from '@aztec/stdlib/abi'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { parseSequencerAllowList } from './config.js'; +import { parseAllowList } from './config.js'; -describe('sequencer config', () => { - it('parse a sequencer config', async () => { +describe('config', () => { + it('parses allow list', async () => { const instance = { address: await AztecAddress.random() }; const instanceFunction = { address: await AztecAddress.random(), selector: FunctionSelector.random() }; const classId = { classId: Fr.random() }; @@ -21,7 +21,7 @@ describe('sequencer config', () => { ]; const stringifiedAllowList = configStrings.join(','); - const allowList = parseSequencerAllowList(stringifiedAllowList); + const allowList = parseAllowList(stringifiedAllowList); expect(allowList).toEqual(config); }); }); diff --git a/yarn-project/p2p/src/config.ts b/yarn-project/p2p/src/config.ts index ce45457b02d1..d8dc390205d2 100644 --- a/yarn-project/p2p/src/config.ts +++ b/yarn-project/p2p/src/config.ts @@ -6,8 +6,11 @@ import { numberConfigHelper, pickConfigMappings, } from '@aztec/foundation/config'; +import { Fr } from '@aztec/foundation/fields'; import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; -import { type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config'; +import { FunctionSelector } from '@aztec/stdlib/abi'; +import { AztecAddress } from '@aztec/stdlib/aztec-address'; +import { type AllowedElement, type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config'; import { type P2PReqRespConfig, p2pReqRespConfigMappings } from './services/reqresp/config.js'; @@ -169,6 +172,9 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig { * The maximum possible size of the P2P DB in KB. Overwrites the general dataStoreMapSizeKB. */ p2pStoreMapSizeKb?: number; + + /** Which calls are allowed in the public setup phase of a tx. */ + txPublicSetupAllowList: AllowedElement[]; } export const p2pConfigMappings: ConfigMappingsType = { @@ -341,6 +347,13 @@ export const p2pConfigMappings: ConfigMappingsType = { parseEnv: (val: string | undefined) => (val ? +val : undefined), description: 'The maximum possible size of the P2P DB in KB. Overwrites the general dataStoreMapSizeKB.', }, + txPublicSetupAllowList: { + env: 'TX_PUBLIC_SETUP_ALLOWLIST', + parseEnv: (val: string) => parseAllowList(val), + description: 'The list of functions calls allowed to run in setup', + printDefault: () => + 'AuthRegistry, FeeJuice.increase_public_balance, Token.increase_public_balance, FPC.prepare_fee', + }, ...p2pReqRespConfigMappings, ...chainConfigMappings, }; @@ -383,3 +396,53 @@ export const bootnodeConfigMappings = pickConfigMappings( { ...p2pConfigMappings, ...dataConfigMappings, ...chainConfigMappings }, bootnodeConfigKeys, ); + +/** + * Parses a string to a list of allowed elements. + * Each encoded is expected to be of one of the following formats + * `I:${address}` + * `I:${address}:${selector}` + * `C:${classId}` + * `C:${classId}:${selector}` + * + * @param value The string to parse + * @returns A list of allowed elements + */ +export function parseAllowList(value: string): AllowedElement[] { + const entries: AllowedElement[] = []; + + if (!value) { + return entries; + } + + for (const val of value.split(',')) { + const [typeString, identifierString, selectorString] = val.split(':'); + const selector = selectorString !== undefined ? FunctionSelector.fromString(selectorString) : undefined; + + if (typeString === 'I') { + if (selector) { + entries.push({ + address: AztecAddress.fromString(identifierString), + selector, + }); + } else { + entries.push({ + address: AztecAddress.fromString(identifierString), + }); + } + } else if (typeString === 'C') { + if (selector) { + entries.push({ + classId: Fr.fromHexString(identifierString), + selector, + }); + } else { + entries.push({ + classId: Fr.fromHexString(identifierString), + }); + } + } + } + + return entries; +} diff --git a/yarn-project/sequencer-client/src/sequencer/allowed.ts b/yarn-project/p2p/src/msg_validators/tx_validator/allowed_public_setup.ts similarity index 99% rename from yarn-project/sequencer-client/src/sequencer/allowed.ts rename to yarn-project/p2p/src/msg_validators/tx_validator/allowed_public_setup.ts index 7afa2fb5a597..64578772ea82 100644 --- a/yarn-project/sequencer-client/src/sequencer/allowed.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/allowed_public_setup.ts @@ -5,7 +5,6 @@ import { getContractClassFromArtifact } from '@aztec/stdlib/contract'; import type { AllowedElement } from '@aztec/stdlib/interfaces/server'; let defaultAllowedSetupFunctions: AllowedElement[] | undefined = undefined; - export async function getDefaultAllowedSetupFunctions(): Promise { if (defaultAllowedSetupFunctions === undefined) { defaultAllowedSetupFunctions = [ diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/gas_validator.test.ts similarity index 97% rename from yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts rename to yarn-project/p2p/src/msg_validators/tx_validator/gas_validator.test.ts index 0e553c0debf7..46ac4608fa96 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/gas_validator.test.ts @@ -6,11 +6,12 @@ import { FunctionSelector } from '@aztec/stdlib/abi'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { GasFees, GasSettings } from '@aztec/stdlib/gas'; import { mockTx } from '@aztec/stdlib/testing'; +import type { PublicStateSource } from '@aztec/stdlib/trees'; import type { Tx } from '@aztec/stdlib/tx'; import { type MockProxy, mock, mockFn } from 'jest-mock-extended'; -import { GasTxValidator, type PublicStateSource } from './gas_validator.js'; +import { GasTxValidator } from './gas_validator.js'; import { patchNonRevertibleFn, patchRevertibleFn } from './test_utils.js'; describe('GasTxValidator', () => { diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/gas_validator.ts similarity index 94% rename from yarn-project/sequencer-client/src/tx_validator/gas_validator.ts rename to yarn-project/p2p/src/msg_validators/tx_validator/gas_validator.ts index ce68b853a77b..b6760290d83e 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/gas_validator.ts @@ -1,17 +1,12 @@ -import { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice'; import { getCallRequestsWithCalldataByPhase } from '@aztec/simulator/server'; import { FunctionSelector } from '@aztec/stdlib/abi'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { GasFees } from '@aztec/stdlib/gas'; +import type { PublicStateSource } from '@aztec/stdlib/trees'; import { type Tx, TxExecutionPhase, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx'; -/** Provides a view into public contract state */ -export interface PublicStateSource { - storageRead: (contractAddress: AztecAddress, slot: Fr) => Promise; -} - export class GasTxValidator implements TxValidator { #log = createLogger('sequencer:tx_validator:tx_gas'); #publicDataSource: PublicStateSource; 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 de02c4d7d966..09ccbfa163eb 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/index.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/index.ts @@ -4,3 +4,7 @@ export * from './double_spend_validator.js'; export * from './metadata_validator.js'; export * from './tx_proof_validator.js'; export * from './block_header_validator.js'; +export * from './gas_validator.js'; +export * from './phases_validator.js'; +export * from './test_utils.js'; +export * from './allowed_public_setup.js'; diff --git a/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.test.ts similarity index 100% rename from yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts rename to yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.test.ts diff --git a/yarn-project/sequencer-client/src/tx_validator/phases_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.ts similarity index 98% rename from yarn-project/sequencer-client/src/tx_validator/phases_validator.ts rename to yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.ts index e69be753429f..e69301fcd1d2 100644 --- a/yarn-project/sequencer-client/src/tx_validator/phases_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/phases_validator.ts @@ -3,7 +3,7 @@ import { PublicContractsDB, getCallRequestsWithCalldataByPhase } from '@aztec/si import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { AllowedElement } from '@aztec/stdlib/interfaces/server'; import { - PublicCallRequestWithCalldata, + type PublicCallRequestWithCalldata, Tx, TxExecutionPhase, type TxValidationResult, diff --git a/yarn-project/sequencer-client/src/tx_validator/test_utils.ts b/yarn-project/p2p/src/msg_validators/tx_validator/test_utils.ts similarity index 100% rename from yarn-project/sequencer-client/src/tx_validator/test_utils.ts rename to yarn-project/p2p/src/msg_validators/tx_validator/test_utils.ts diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index 3afa90be7350..15347435837b 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -4,7 +4,10 @@ import { createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log import { SerialQueue } from '@aztec/foundation/queue'; import { RunningPromise } from '@aztec/foundation/running-promise'; import type { AztecAsyncKVStore } from '@aztec/kv-store'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import type { L2BlockSource } from '@aztec/stdlib/block'; +import type { ContractDataSource } from '@aztec/stdlib/contract'; +import { GasFees } from '@aztec/stdlib/gas'; import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; import { BlockAttestation, @@ -16,7 +19,7 @@ import { getTopicTypeForClientType, metricsTopicStrToLabels, } from '@aztec/stdlib/p2p'; -import { MerkleTreeId } from '@aztec/stdlib/trees'; +import { DatabasePublicStateSource, MerkleTreeId } from '@aztec/stdlib/trees'; import { Tx, type TxHash, type TxValidationResult } from '@aztec/stdlib/tx'; import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client'; @@ -43,10 +46,13 @@ import { createLibp2p } from 'libp2p'; import type { P2PConfig } from '../../config.js'; import type { MemPools } from '../../mem_pools/interface.js'; import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js'; +import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js'; import { DataTxValidator, DoubleSpendTxValidator, + GasTxValidator, MetadataTxValidator, + PhasesTxValidator, TxProofValidator, } from '../../msg_validators/tx_validator/index.js'; import { GossipSubEvent } from '../../types/index.js'; @@ -95,6 +101,8 @@ export class LibP2PService extends WithTracer implement // Trusted peers ids private trustedPeersIds: PeerId[] = []; + private feesCache: { blockNumber: number; gasFees: GasFees } | undefined; + /** * Callback for when a block is received from a peer. * @param block - The block received from the peer. @@ -110,7 +118,7 @@ export class LibP2PService extends WithTracer implement private node: PubSubLibp2p, private peerDiscoveryService: PeerDiscoveryService, private mempools: MemPools, - private l2BlockSource: L2BlockSource, + private archiver: L2BlockSource & ContractDataSource, epochCache: EpochCacheInterface, private proofVerifier: ClientProtocolCircuitVerifier, private worldStateSynchronizer: WorldStateSynchronizer, @@ -164,7 +172,7 @@ export class LibP2PService extends WithTracer implement peerDiscoveryService: PeerDiscoveryService, peerId: PeerId, mempools: MemPools, - l2BlockSource: L2BlockSource, + l2BlockSource: L2BlockSource & ContractDataSource, epochCache: EpochCacheInterface, proofVerifier: ClientProtocolCircuitVerifier, worldStateSynchronizer: WorldStateSynchronizer, @@ -322,7 +330,7 @@ export class LibP2PService extends WithTracer implement // Create request response protocol handlers const txHandler = reqRespTxHandler(this.mempools); const goodbyeHandler = reqGoodbyeHandler(this.peerManager); - const blockHandler = reqRespBlockHandler(this.l2BlockSource); + const blockHandler = reqRespBlockHandler(this.archiver); const requestResponseHandlers = { [ReqRespSubProtocol.PING]: pingHandler, @@ -686,8 +694,8 @@ export class LibP2PService extends WithTracer implement [Attributes.TX_HASH]: (await tx.getTxHash()).toString(), })) private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise { - const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1; - const messageValidators = this.createMessageValidators(blockNumber); + const blockNumber = (await this.archiver.getBlockNumber()) + 1; + const messageValidators = await this.createMessageValidators(blockNumber); const outcome = await this.runValidations(tx, messageValidators); if (outcome.allPassed) { @@ -705,6 +713,17 @@ export class LibP2PService extends WithTracer implement return false; } + private async getGasFees(blockNumber: number): Promise { + if (blockNumber === this.feesCache?.blockNumber) { + return this.feesCache.gasFees; + } + + const header = await this.archiver.getBlockHeader(blockNumber); + const gasFees = header?.globalVariables.gasFees ?? GasFees.empty(); + this.feesCache = { blockNumber, gasFees }; + return gasFees; + } + /** * Create message validators for the given block number. * @@ -714,7 +733,11 @@ export class LibP2PService extends WithTracer implement * @param blockNumber - The block number to create validators for. * @returns The message validators. */ - private createMessageValidators(blockNumber: number): Record { + private async createMessageValidators(blockNumber: number): Promise> { + const merkleTree = this.worldStateSynchronizer.getCommitted(); + const gasFees = await this.getGasFees(blockNumber - 1); + const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions()); + return { dataValidator: { validator: new DataTxValidator(), @@ -742,6 +765,18 @@ export class LibP2PService extends WithTracer implement }), severity: PeerErrorSeverity.HighToleranceError, }, + gasValidator: { + validator: new GasTxValidator( + new DatabasePublicStateSource(merkleTree), + ProtocolContractAddress.FeeJuice, + gasFees, + ), + severity: PeerErrorSeverity.HighToleranceError, + }, + phasesValidator: { + validator: new PhasesTxValidator(this.archiver, allowedInSetup, blockNumber), + severity: PeerErrorSeverity.MidToleranceError, + }, }; } diff --git a/yarn-project/p2p/src/test-helpers/reqresp-nodes.ts b/yarn-project/p2p/src/test-helpers/reqresp-nodes.ts index 0378a585e0ef..89a04dc70106 100644 --- a/yarn-project/p2p/src/test-helpers/reqresp-nodes.ts +++ b/yarn-project/p2p/src/test-helpers/reqresp-nodes.ts @@ -4,6 +4,7 @@ import type { DataStoreConfig } from '@aztec/kv-store/config'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import type { L2BlockSource } from '@aztec/stdlib/block'; import { type ChainConfig, emptyChainConfig } from '@aztec/stdlib/config'; +import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { ClientProtocolCircuitVerifier, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; import type { P2PClientType } from '@aztec/stdlib/p2p'; import type { Tx } from '@aztec/stdlib/tx'; @@ -98,7 +99,7 @@ export async function createLibp2pNode( export async function createTestLibP2PService( clientType: T, boostrapAddrs: string[] = [], - l2BlockSource: L2BlockSource, + archiver: L2BlockSource & ContractDataSource, worldStateSynchronizer: WorldStateSynchronizer, epochCache: EpochCache, mempools: MemPools, @@ -131,7 +132,7 @@ export async function createTestLibP2PService( p2pNode as PubSubLibp2p, discoveryService, mempools, - l2BlockSource, + archiver, epochCache, proofVerifier, worldStateSynchronizer, diff --git a/yarn-project/p2p/tsconfig.json b/yarn-project/p2p/tsconfig.json index 07a8d5deb658..b998de96930c 100644 --- a/yarn-project/p2p/tsconfig.json +++ b/yarn-project/p2p/tsconfig.json @@ -18,12 +18,18 @@ { "path": "../kv-store" }, + { + "path": "../noir-contracts.js" + }, { "path": "../noir-protocol-circuits-types" }, { "path": "../protocol-contracts" }, + { + "path": "../simulator" + }, { "path": "../stdlib" }, diff --git a/yarn-project/sequencer-client/src/config.ts b/yarn-project/sequencer-client/src/config.ts index 967ffe542edf..f39056ed737f 100644 --- a/yarn-project/sequencer-client/src/config.ts +++ b/yarn-project/sequencer-client/src/config.ts @@ -12,10 +12,9 @@ import { pickConfigMappings, } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr } from '@aztec/foundation/fields'; -import { FunctionSelector } from '@aztec/stdlib/abi'; +import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { type AllowedElement, type ChainConfig, type SequencerConfig, chainConfigMappings } from '@aztec/stdlib/config'; +import { type ChainConfig, type SequencerConfig, chainConfigMappings } from '@aztec/stdlib/config'; import { type ValidatorClientConfig, validatorClientConfigMappings } from '@aztec/validator-client'; import { @@ -37,6 +36,7 @@ export type SequencerClientConfig = PublisherConfig & SequencerConfig & L1ReaderConfig & ChainConfig & + Pick & Pick; export const sequencerConfigMappings: ConfigMappingsType = { @@ -83,13 +83,6 @@ export const sequencerConfigMappings: ConfigMappingsType = { env: 'ACVM_BINARY_PATH', description: 'The path to the ACVM binary', }, - allowedInSetup: { - env: 'SEQ_ALLOWED_SETUP_FN', - parseEnv: (val: string) => parseSequencerAllowList(val), - description: 'The list of functions calls allowed to run in setup', - printDefault: () => - 'AuthRegistry, FeeJuice.increase_public_balance, Token.increase_public_balance, FPC.prepare_fee', - }, maxBlockSizeInBytes: { env: 'SEQ_MAX_BLOCK_SIZE_IN_BYTES', description: 'Max block size', @@ -112,6 +105,7 @@ export const sequencerConfigMappings: ConfigMappingsType = { description: 'How many seconds into an L1 slot we can still send a tx and get it mined.', parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined), }, + ...pickConfigMappings(p2pConfigMappings, ['txPublicSetupAllowList']), }; export const sequencerClientConfigMappings: ConfigMappingsType = { @@ -130,53 +124,3 @@ export const sequencerClientConfigMappings: ConfigMappingsType(sequencerClientConfigMappings); } - -/** - * Parses a string to a list of allowed elements. - * Each encoded is expected to be of one of the following formats - * `I:${address}` - * `I:${address}:${selector}` - * `C:${classId}` - * `C:${classId}:${selector}` - * - * @param value The string to parse - * @returns A list of allowed elements - */ -export function parseSequencerAllowList(value: string): AllowedElement[] { - const entries: AllowedElement[] = []; - - if (!value) { - return entries; - } - - for (const val of value.split(',')) { - const [typeString, identifierString, selectorString] = val.split(':'); - const selector = selectorString !== undefined ? FunctionSelector.fromString(selectorString) : undefined; - - if (typeString === 'I') { - if (selector) { - entries.push({ - address: AztecAddress.fromString(identifierString), - selector, - }); - } else { - entries.push({ - address: AztecAddress.fromString(identifierString), - }); - } - } else if (typeString === 'C') { - if (selector) { - entries.push({ - classId: Fr.fromHexString(identifierString), - selector, - }); - } else { - entries.push({ - classId: Fr.fromHexString(identifierString), - }); - } - } - } - - return entries; -} diff --git a/yarn-project/sequencer-client/src/index.ts b/yarn-project/sequencer-client/src/index.ts index d5fc13c50efa..8cc80d39d616 100644 --- a/yarn-project/sequencer-client/src/index.ts +++ b/yarn-project/sequencer-client/src/index.ts @@ -3,7 +3,7 @@ export * from './config.js'; export * from './publisher/index.js'; export * from './tx_validator/tx_validator_factory.js'; export * from './slasher/index.js'; -export { Sequencer, SequencerState, getDefaultAllowedSetupFunctions } from './sequencer/index.js'; +export { Sequencer, SequencerState } from './sequencer/index.js'; // Used by the node to simulate public parts of transactions. Should these be moved to a shared library? // ISSUE(#9832) diff --git a/yarn-project/sequencer-client/src/sequencer/index.ts b/yarn-project/sequencer-client/src/sequencer/index.ts index 316084b13f13..459a5cab42ff 100644 --- a/yarn-project/sequencer-client/src/sequencer/index.ts +++ b/yarn-project/sequencer-client/src/sequencer/index.ts @@ -1,3 +1,2 @@ export * from './config.js'; export * from './sequencer.js'; -export * from './allowed.js'; diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 0f68f9e9d3d8..93b1566380f6 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -8,6 +8,7 @@ import { createLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; import { type DateProvider, Timer, elapsed } from '@aztec/foundation/timer'; import type { P2P } from '@aztec/p2p'; +import { getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators'; import type { BlockBuilderFactory } from '@aztec/prover-client/block-builder'; import type { PublicProcessorFactory } from '@aztec/simulator/server'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; @@ -47,7 +48,6 @@ import type { GlobalVariableBuilder } from '../global_variable_builder/global_bu import { type SequencerPublisher, VoteType } from '../publisher/sequencer-publisher.js'; import type { SlasherClient } from '../slasher/slasher_client.js'; import { createValidatorForBlockBuilding } from '../tx_validator/tx_validator_factory.js'; -import { getDefaultAllowedSetupFunctions } from './allowed.js'; import type { SequencerConfig } from './config.js'; import { SequencerMetrics } from './metrics.js'; import { SequencerTimetable, SequencerTooSlowError } from './timetable.js'; @@ -76,7 +76,7 @@ export class Sequencer { private _coinbase = EthAddress.ZERO; private _feeRecipient = AztecAddress.ZERO; private state = SequencerState.STOPPED; - private allowedInSetup: AllowedElement[] = []; + private txPublicSetupAllowList: AllowedElement[] = []; private maxBlockSizeInBytes: number = 1024 * 1024; private maxBlockGas: Gas = new Gas(100e9, 100e9); private metrics: SequencerMetrics; @@ -131,7 +131,10 @@ export class Sequencer { * @param config - New parameters. */ public async updateConfig(config: SequencerConfig) { - this.log.info(`Sequencer config set`, omit(pickFromSchema(config, SequencerConfigSchema), 'allowedInSetup')); + this.log.info( + `Sequencer config set`, + omit(pickFromSchema(config, SequencerConfigSchema), 'txPublicSetupAllowList'), + ); if (config.transactionPollingIntervalMS !== undefined) { this.pollingIntervalMs = config.transactionPollingIntervalMS; @@ -154,10 +157,10 @@ export class Sequencer { if (config.feeRecipient) { this._feeRecipient = config.feeRecipient; } - if (config.allowedInSetup) { - this.allowedInSetup = config.allowedInSetup; + if (config.txPublicSetupAllowList) { + this.txPublicSetupAllowList = config.txPublicSetupAllowList; } else { - this.allowedInSetup = await getDefaultAllowedSetupFunctions(); + this.txPublicSetupAllowList = await getDefaultAllowedSetupFunctions(); } if (config.maxBlockSizeInBytes !== undefined) { this.maxBlockSizeInBytes = config.maxBlockSizeInBytes; @@ -469,7 +472,7 @@ export class Sequencer { publicProcessorDBFork, this.contractDataSource, newGlobalVariables, - this.allowedInSetup, + this.txPublicSetupAllowList, ); // TODO(#11000): Public processor should just handle processing, one tx at a time. It should be responsibility diff --git a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts index bdb7f104f46a..4c0115952a9a 100644 --- a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts +++ b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts @@ -1,30 +1,27 @@ -import { MerkleTreeId } from '@aztec/aztec.js'; import { Fr } from '@aztec/foundation/fields'; import { AggregateTxValidator, BlockHeaderTxValidator, DataTxValidator, DoubleSpendTxValidator, + GasTxValidator, MetadataTxValidator, + PhasesTxValidator, TxProofValidator, } from '@aztec/p2p'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; -import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { GasFees } from '@aztec/stdlib/gas'; -import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash'; import type { AllowedElement, ClientProtocolCircuitVerifier, MerkleTreeReadOperations, } from '@aztec/stdlib/interfaces/server'; -import type { PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees'; +import { DatabasePublicStateSource, type PublicStateSource } from '@aztec/stdlib/trees'; import { GlobalVariables, type Tx, type TxValidator } from '@aztec/stdlib/tx'; import { ArchiveCache } from './archive_cache.js'; -import { GasTxValidator, type PublicStateSource } from './gas_validator.js'; import { NullifierCache } from './nullifier_cache.js'; -import { PhasesTxValidator } from './phases_validator.js'; export function createValidatorForAcceptingTxs( db: MerkleTreeReadOperations, @@ -91,26 +88,6 @@ export function createValidatorForBlockBuilding( }; } -class DatabasePublicStateSource implements PublicStateSource { - constructor(private db: MerkleTreeReadOperations) {} - - async storageRead(contractAddress: AztecAddress, slot: Fr): Promise { - const leafSlot = (await computePublicDataTreeLeafSlot(contractAddress, slot)).toBigInt(); - - const lowLeafResult = await this.db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); - if (!lowLeafResult || !lowLeafResult.alreadyPresent) { - return Fr.ZERO; - } - - const preimage = (await this.db.getLeafPreimage( - MerkleTreeId.PUBLIC_DATA_TREE, - lowLeafResult.index, - )) as PublicDataTreeLeafPreimage; - - return preimage.leaf.value; - } -} - function preprocessValidator( nullifierCache: NullifierCache, archiveCache: ArchiveCache, diff --git a/yarn-project/stdlib/src/config/config.ts b/yarn-project/stdlib/src/config/config.ts index b3a74e462ced..c0292fe76e8f 100644 --- a/yarn-project/stdlib/src/config/config.ts +++ b/yarn-project/stdlib/src/config/config.ts @@ -2,7 +2,8 @@ import { l1ContractAddressesMapping } from '@aztec/ethereum/l1-contract-addresse import type { ConfigMappingsType } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; -export { type AllowedElement, type SequencerConfig, SequencerConfigSchema } from '../interfaces/configs.js'; +export { type SequencerConfig, SequencerConfigSchema } from '../interfaces/configs.js'; +export { type AllowedElement } from '../interfaces/allowed_element.js'; export const emptyChainConfig: ChainConfig = { l1ChainId: 0, diff --git a/yarn-project/stdlib/src/interfaces/allowed_element.ts b/yarn-project/stdlib/src/interfaces/allowed_element.ts new file mode 100644 index 000000000000..29d92f992b04 --- /dev/null +++ b/yarn-project/stdlib/src/interfaces/allowed_element.ts @@ -0,0 +1,21 @@ +import type { Fr } from '@aztec/foundation/fields'; + +import { z } from 'zod'; + +import type { FunctionSelector } from '../abi/function_selector.js'; +import type { AztecAddress } from '../aztec-address/index.js'; +import { type ZodFor, schemas } from '../schemas/index.js'; + +type AllowedInstance = { address: AztecAddress }; +type AllowedInstanceFunction = { address: AztecAddress; selector: FunctionSelector }; +type AllowedClass = { classId: Fr }; +type AllowedClassFunction = { classId: Fr; selector: FunctionSelector }; + +export type AllowedElement = AllowedInstance | AllowedInstanceFunction | AllowedClass | AllowedClassFunction; + +export const AllowedElementSchema = z.union([ + z.object({ address: schemas.AztecAddress, selector: schemas.FunctionSelector }), + z.object({ address: schemas.AztecAddress }), + z.object({ classId: schemas.Fr, selector: schemas.FunctionSelector }), + z.object({ classId: schemas.Fr }), +]) satisfies ZodFor; diff --git a/yarn-project/stdlib/src/interfaces/configs.ts b/yarn-project/stdlib/src/interfaces/configs.ts index 170f087e6211..0cd5662246be 100644 --- a/yarn-project/stdlib/src/interfaces/configs.ts +++ b/yarn-project/stdlib/src/interfaces/configs.ts @@ -1,18 +1,10 @@ import type { EthAddress } from '@aztec/foundation/eth-address'; -import type { Fr } from '@aztec/foundation/fields'; import { z } from 'zod'; -import type { FunctionSelector } from '../abi/function_selector.js'; import type { AztecAddress } from '../aztec-address/index.js'; import { type ZodFor, schemas } from '../schemas/index.js'; - -type AllowedInstance = { address: AztecAddress }; -type AllowedInstanceFunction = { address: AztecAddress; selector: FunctionSelector }; -type AllowedClass = { classId: Fr }; -type AllowedClassFunction = { classId: Fr; selector: FunctionSelector }; - -export type AllowedElement = AllowedInstance | AllowedInstanceFunction | AllowedClass | AllowedClassFunction; +import { type AllowedElement, AllowedElementSchema } from './allowed_element.js'; /** * The sequencer configuration. @@ -37,7 +29,7 @@ export interface SequencerConfig { /** The path to the ACVM binary */ acvmBinaryPath?: string; /** The list of functions calls allowed to run in setup */ - allowedInSetup?: AllowedElement[]; + txPublicSetupAllowList?: AllowedElement[]; /** Max block size */ maxBlockSizeInBytes?: number; /** Payload address to vote for */ @@ -48,13 +40,6 @@ export interface SequencerConfig { maxL1TxInclusionTimeIntoSlot?: number; } -const AllowedElementSchema = z.union([ - z.object({ address: schemas.AztecAddress, selector: schemas.FunctionSelector }), - z.object({ address: schemas.AztecAddress }), - z.object({ classId: schemas.Fr, selector: schemas.FunctionSelector }), - z.object({ classId: schemas.Fr }), -]) satisfies ZodFor; - export const SequencerConfigSchema = z.object({ transactionPollingIntervalMS: z.number().optional(), maxTxsPerBlock: z.number().optional(), @@ -65,7 +50,7 @@ export const SequencerConfigSchema = z.object({ feeRecipient: schemas.AztecAddress.optional(), acvmWorkingDirectory: z.string().optional(), acvmBinaryPath: z.string().optional(), - allowedInSetup: z.array(AllowedElementSchema).optional(), + txPublicSetupAllowList: z.array(AllowedElementSchema).optional(), maxBlockSizeInBytes: z.number().optional(), governanceProposerPayload: schemas.EthAddress.optional(), maxL1TxInclusionTimeIntoSlot: z.number().optional(), diff --git a/yarn-project/stdlib/src/interfaces/public_state_source.ts b/yarn-project/stdlib/src/interfaces/public_state_source.ts new file mode 100644 index 000000000000..7123b3e833e5 --- /dev/null +++ b/yarn-project/stdlib/src/interfaces/public_state_source.ts @@ -0,0 +1,9 @@ +import type { Fr } from '@aztec/foundation/fields'; + +import type { AztecAddress } from '../aztec-address/index.js'; + +/** Provides a view into public contract state */ +export interface PublicStateSource { + /** Returns the value for a given slot at a given contract. */ + storageRead: (contractAddress: AztecAddress, slot: Fr) => Promise; +} diff --git a/yarn-project/stdlib/src/interfaces/server.ts b/yarn-project/stdlib/src/interfaces/server.ts index d7b0416cac3e..ae6028ffbfcb 100644 --- a/yarn-project/stdlib/src/interfaces/server.ts +++ b/yarn-project/stdlib/src/interfaces/server.ts @@ -17,3 +17,4 @@ export * from './p2p.js'; export * from './p2p-bootstrap.js'; export * from './l2_logs_source.js'; export * from './merkle_tree_operations.js'; +export * from './allowed_element.js'; diff --git a/yarn-project/stdlib/src/trees/database_public_state_source.ts b/yarn-project/stdlib/src/trees/database_public_state_source.ts new file mode 100644 index 000000000000..a4689bf23383 --- /dev/null +++ b/yarn-project/stdlib/src/trees/database_public_state_source.ts @@ -0,0 +1,30 @@ +import { Fr } from '@aztec/foundation/fields'; + +import type { AztecAddress } from '../aztec-address/index.js'; +import { computePublicDataTreeLeafSlot } from '../hash/hash.js'; +import type { MerkleTreeReadOperations } from '../interfaces/merkle_tree_operations.js'; +import type { PublicStateSource } from '../interfaces/public_state_source.js'; +import { MerkleTreeId } from './merkle_tree_id.js'; +import type { PublicDataTreeLeafPreimage } from './public_data_leaf.js'; + +export type { PublicStateSource }; + +export class DatabasePublicStateSource implements PublicStateSource { + constructor(private db: MerkleTreeReadOperations) {} + + async storageRead(contractAddress: AztecAddress, slot: Fr): Promise { + const leafSlot = (await computePublicDataTreeLeafSlot(contractAddress, slot)).toBigInt(); + + const lowLeafResult = await this.db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); + if (!lowLeafResult || !lowLeafResult.alreadyPresent) { + return Fr.ZERO; + } + + const preimage = (await this.db.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + + return preimage.leaf.value; + } +} diff --git a/yarn-project/stdlib/src/trees/index.ts b/yarn-project/stdlib/src/trees/index.ts index 4acaf3de8b06..d7f1c087046c 100644 --- a/yarn-project/stdlib/src/trees/index.ts +++ b/yarn-project/stdlib/src/trees/index.ts @@ -6,3 +6,4 @@ export * from './merkle_tree_id.js'; export * from './nullifier_membership_witness.js'; export * from './public_data_witness.js'; export * from '../interfaces/merkle_tree_operations.js'; +export * from './database_public_state_source.js'; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 4a6cf09d3de5..4957063aaaa0 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -1083,8 +1083,10 @@ __metadata: "@aztec/epoch-cache": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/kv-store": "workspace:^" + "@aztec/noir-contracts.js": "workspace:^" "@aztec/noir-protocol-circuits-types": "workspace:^" "@aztec/protocol-contracts": "workspace:^" + "@aztec/simulator": "workspace:^" "@aztec/stdlib": "workspace:^" "@aztec/telemetry-client": "workspace:^" "@chainsafe/discv5": "npm:9.0.0"