diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts index 03fc6cd310c9..3c01bd969465 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -34,6 +34,7 @@ describe('aztec node', () => { let feePayer: AztecAddress; const chainId = new Fr(12345); + const rollupVersion = new Fr(1); const mockTxForRollup = async (seed: number) => { return await mockTx(seed, { @@ -117,7 +118,7 @@ describe('aztec node', () => { undefined, undefined, 12345, - 1, + rollupVersion.toNumber(), globalVariablesBuilder, new TestCircuitVerifier(), ); @@ -128,6 +129,7 @@ describe('aztec node', () => { const txs = await Promise.all([mockTxForRollup(0x10000), mockTxForRollup(0x20000)]); txs.forEach(tx => { tx.data.constants.txContext.chainId = chainId; + tx.data.constants.txContext.version = rollupVersion; }); const doubleSpendTx = txs[0]; const doubleSpendWithExistingTx = txs[1]; @@ -164,6 +166,7 @@ describe('aztec node', () => { it('tests that the node correctly validates chain id', async () => { const tx = await mockTxForRollup(0x10000); tx.data.constants.txContext.chainId = chainId; + tx.data.constants.txContext.version = rollupVersion; expect(await node.isValidTx(tx)).toEqual({ result: 'valid' }); @@ -173,10 +176,24 @@ describe('aztec node', () => { expect(await node.isValidTx(tx)).toEqual({ result: 'invalid', reason: ['Incorrect chain id'] }); }); + it('tests that the node correctly validates rollup version', async () => { + const tx = await mockTxForRollup(0x10000); + tx.data.constants.txContext.chainId = chainId; + tx.data.constants.txContext.version = rollupVersion; + + expect(await node.isValidTx(tx)).toEqual({ result: 'valid' }); + + // We make the chain id on the tx not equal to the configured chain id + tx.data.constants.txContext.version = new Fr(1n + rollupVersion.toBigInt()); + + expect(await node.isValidTx(tx)).toEqual({ result: 'invalid', reason: ['Incorrect rollup version'] }); + }); + it('tests that the node correctly validates max block numbers', async () => { const txs = await Promise.all([mockTxForRollup(0x10000), mockTxForRollup(0x20000), mockTxForRollup(0x30000)]); txs.forEach(tx => { tx.data.constants.txContext.chainId = chainId; + tx.data.constants.txContext.version = rollupVersion; }); const noMaxBlockNumberMetadata = txs[0]; diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index ca4a0bc7c315..68c5bba1be01 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -988,6 +988,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { const validator = createValidatorForAcceptingTxs(db, this.contractDataSource, verifier, { blockNumber, l1ChainId: this.l1ChainId, + rollupVersion: this.version, setupAllowList: this.config.allowedInSetup ?? (await getDefaultAllowedSetupFunctions()), gasFees: await this.getCurrentBaseFees(), skipFeeEnforcement, diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.test.ts index 52081e937753..726c81842b50 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.test.ts @@ -8,12 +8,14 @@ import { MetadataTxValidator } from './metadata_validator.js'; describe('MetadataTxValidator', () => { let blockNumber: Fr; let chainId: Fr; + let rollupVersion: Fr; let validator: MetadataTxValidator; beforeEach(() => { chainId = new Fr(1); blockNumber = new Fr(42); - validator = new MetadataTxValidator(chainId, blockNumber); + rollupVersion = new Fr(2); + validator = new MetadataTxValidator(chainId, rollupVersion, blockNumber); }); const expectValid = async (tx: Tx) => { @@ -30,10 +32,12 @@ describe('MetadataTxValidator', () => { goodTxs.forEach(tx => { tx.data.constants.txContext.chainId = chainId; + tx.data.constants.txContext.version = rollupVersion; }); badTxs.forEach(tx => { tx.data.constants.txContext.chainId = chainId.add(new Fr(1)); + tx.data.constants.txContext.version = rollupVersion; }); await expectValid(goodTxs[0]); @@ -42,9 +46,30 @@ describe('MetadataTxValidator', () => { await expectInvalid(badTxs[1], 'Incorrect chain id'); }); + it('allows only transactions for the right rollup', async () => { + const goodTxs = await Promise.all([mockTx(1), mockTxForRollup(2)]); + const badTxs = await Promise.all([mockTx(3), mockTxForRollup(4)]); + + goodTxs.forEach(tx => { + tx.data.constants.txContext.chainId = chainId; + tx.data.constants.txContext.version = rollupVersion; + }); + + badTxs.forEach(tx => { + tx.data.constants.txContext.chainId = chainId; + tx.data.constants.txContext.version = rollupVersion.add(Fr.ONE); + }); + + await expectValid(goodTxs[0]); + await expectValid(goodTxs[1]); + await expectInvalid(badTxs[0], 'Incorrect rollup version'); + await expectInvalid(badTxs[1], 'Incorrect rollup version'); + }); + it.each([42, 43])('allows txs with valid max block number', async maxBlockNumber => { const goodTx = await mockTxForRollup(1); goodTx.data.constants.txContext.chainId = chainId; + goodTx.data.constants.txContext.version = rollupVersion; goodTx.data.rollupValidationRequests.maxBlockNumber = new MaxBlockNumber(true, new Fr(maxBlockNumber)); await expectValid(goodTx); @@ -53,6 +78,7 @@ describe('MetadataTxValidator', () => { it('allows txs with unset max block number', async () => { const goodTx = await mockTxForRollup(1); goodTx.data.constants.txContext.chainId = chainId; + goodTx.data.constants.txContext.version = rollupVersion; goodTx.data.rollupValidationRequests.maxBlockNumber = new MaxBlockNumber(false, Fr.ZERO); await expectValid(goodTx); @@ -61,6 +87,7 @@ describe('MetadataTxValidator', () => { it('rejects txs with lower max block number', async () => { const badTx = await mockTxForRollup(1); badTx.data.constants.txContext.chainId = chainId; + badTx.data.constants.txContext.version = rollupVersion; badTx.data.rollupValidationRequests.maxBlockNumber = new MaxBlockNumber(true, blockNumber.sub(new Fr(1))); await expectInvalid(badTx, 'Invalid block number'); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.ts index 0d55d434e92d..a610140ca35d 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.ts @@ -5,13 +5,16 @@ import { type AnyTx, Tx, type TxValidationResult, type TxValidator } from '@azte export class MetadataTxValidator implements TxValidator { #log = createLogger('p2p:tx_validator:tx_metadata'); - constructor(private chainId: Fr, private blockNumber: Fr) {} + constructor(private chainId: Fr, private rollupVersion: Fr, private blockNumber: Fr) {} async validateTx(tx: T): Promise { const errors = []; if (!(await this.#hasCorrectChainId(tx))) { errors.push('Incorrect chain id'); } + if (!(await this.#hasCorrectRollupVersion(tx))) { + errors.push('Incorrect rollup version'); + } if (!(await this.#isValidForBlockNumber(tx))) { errors.push('Invalid block number'); } @@ -45,4 +48,17 @@ export class MetadataTxValidator implements TxValidator { return true; } } + + async #hasCorrectRollupVersion(tx: T): Promise { + if (!tx.data.constants.txContext.version.equals(this.rollupVersion)) { + this.#log.warn( + `Rejecting tx ${await Tx.getHash( + tx, + )} because of incorrect rollup version ${tx.data.constants.txContext.version.toNumber()} != ${this.rollupVersion.toNumber()}`, + ); + return false; + } else { + return true; + } + } } diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index f25e2fb59bed..3afa90be7350 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -721,7 +721,11 @@ export class LibP2PService extends WithTracer implement severity: PeerErrorSeverity.HighToleranceError, }, metadataValidator: { - validator: new MetadataTxValidator(new Fr(this.config.l1ChainId), new Fr(blockNumber)), + validator: new MetadataTxValidator( + new Fr(this.config.l1ChainId), + new Fr(this.config.rollupVersion), + new Fr(blockNumber), + ), severity: PeerErrorSeverity.HighToleranceError, }, proofValidator: { 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 1faa6fa54f75..0706ce994913 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 @@ -33,12 +33,14 @@ export function createValidatorForAcceptingTxs( { blockNumber, l1ChainId, + rollupVersion, setupAllowList, gasFees, skipFeeEnforcement, }: { blockNumber: number; l1ChainId: number; + rollupVersion: number; setupAllowList: AllowedElement[]; gasFees: GasFees; skipFeeEnforcement?: boolean; @@ -46,7 +48,7 @@ export function createValidatorForAcceptingTxs( ): TxValidator { const validators: TxValidator[] = [ new DataTxValidator(), - new MetadataTxValidator(new Fr(l1ChainId), new Fr(blockNumber)), + new MetadataTxValidator(new Fr(l1ChainId), new Fr(rollupVersion), new Fr(blockNumber)), new DoubleSpendTxValidator(new NullifierCache(db)), new PhasesTxValidator(contractDataSource, setupAllowList, blockNumber), new BlockHeaderTxValidator(new ArchiveCache(db)), @@ -119,7 +121,7 @@ function preprocessValidator( ): TxValidator { // We don't include the TxProofValidator nor the DataTxValidator here because they are already checked by the time we get to block building. return new AggregateTxValidator( - new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber), + new MetadataTxValidator(globalVariables.chainId, globalVariables.version, globalVariables.blockNumber), new DoubleSpendTxValidator(nullifierCache), new PhasesTxValidator(contractDataSource, setupAllowList, globalVariables.blockNumber.toNumber()), new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, globalVariables.gasFees),