diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts new file mode 100644 index 00000000000..a972190d03b --- /dev/null +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -0,0 +1,17 @@ +import { createEthereumChain } from '@aztec/ethereum'; + +import { AztecNodeConfig, AztecNodeService } from '../index.js'; + +describe('aztec node service', () => { + it('fails to create Aztec Node if given incorrect chain id', async () => { + const config: Partial = { + rpcUrl: 'testnet', + apiKey: '12345', + chainId: 12345, // not the testnet chain id + }; + const ethereumChain = createEthereumChain(config.rpcUrl!, config.apiKey); + await expect(() => AztecNodeService.createAndSync(config as AztecNodeConfig)).rejects.toThrow( + `RPC URL configured for chain id ${ethereumChain.chainInfo.id} but expected id ${config.chainId}`, + ); + }); +}); diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index e379e28c8e1..029415a467a 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -9,7 +9,7 @@ import { PRIVATE_DATA_TREE_HEIGHT, } from '@aztec/circuits.js'; import { computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; -import { L1ContractAddresses } from '@aztec/ethereum'; +import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { createDebugLogger } from '@aztec/foundation/log'; import { InMemoryTxPool, P2P, createP2PClient } from '@aztec/p2p'; @@ -73,7 +73,7 @@ export class AztecNodeService implements AztecNode { private log = createDebugLogger('aztec:node'), ) { const message = - `Started Aztec Node with contracts - \n` + + `Started Aztec Node against chain 0x${chainId.toString(16)} with contracts - \n` + `Rollup: ${config.l1Contracts.rollupAddress.toString()}\n` + `Registry: ${config.l1Contracts.registryAddress.toString()}\n` + `Inbox: ${config.l1Contracts.inboxAddress.toString()}\n` + @@ -88,6 +88,13 @@ export class AztecNodeService implements AztecNode { * @returns - A fully synced Aztec Node for use in development/testing. */ public static async createAndSync(config: AztecNodeConfig) { + const ethereumChain = createEthereumChain(config.rpcUrl, config.apiKey); + //validate that the actual chain id matches that specified in configuration + if (config.chainId !== ethereumChain.chainInfo.id) { + throw new Error( + `RPC URL configured for chain id ${ethereumChain.chainInfo.id} but expected id ${config.chainId}`, + ); + } // first create and sync the archiver const archiver = await Archiver.createAndSync(config); @@ -122,7 +129,7 @@ export class AztecNodeService implements AztecNode { archiver, worldStateSynchronizer, sequencer, - config.chainId, + ethereumChain.chainInfo.id, config.version, getGlobalVariableBuilder(config), db, diff --git a/yarn-project/aztec-node/terraform/main.tf b/yarn-project/aztec-node/terraform/main.tf index 920a11664c1..53e8fe9de1f 100644 --- a/yarn-project/aztec-node/terraform/main.tf +++ b/yarn-project/aztec-node/terraform/main.tf @@ -99,7 +99,7 @@ resource "aws_ecs_task_definition" "aztec-node-1" { [ { "name": "${var.DEPLOY_TAG}-aztec-node-1", - "image": "${var.ECR_URL}/aztec-node:latest", + "image": "${var.ECR_URL}/aztec-node:aztec3-packages-prod", "essential": true, "memoryReservation": 3776, "portMappings": [ @@ -388,7 +388,7 @@ resource "aws_ecs_task_definition" "aztec-node-2" { [ { "name": "${var.DEPLOY_TAG}-aztec-node-2", - "image": "${var.ECR_URL}/aztec-node:latest", + "image": "${var.ECR_URL}/aztec-node:aztec3-packages-prod", "essential": true, "memoryReservation": 3776, "portMappings": [ diff --git a/yarn-project/aztec-sandbox/src/bin/index.ts b/yarn-project/aztec-sandbox/src/bin/index.ts index 184ca5ad707..ca1aa4d16b9 100644 --- a/yarn-project/aztec-sandbox/src/bin/index.ts +++ b/yarn-project/aztec-sandbox/src/bin/index.ts @@ -22,11 +22,21 @@ const logger = createDebugLogger('aztec:sandbox'); * Creates the sandbox from provided config and deploys any initial L1 and L2 contracts */ async function createAndInitialiseSandbox() { - const { l1Contracts, node, pxe, stop } = await createSandbox(); + const { aztecNodeConfig, node, pxe, stop } = await createSandbox(); + if (aztecNodeConfig.p2pEnabled) { + logger.info(`Not setting up test accounts as we are connecting to a network`); + return { + aztecNodeConfig, + pxe, + node, + stop, + accounts: [], + }; + } logger.info('Setting up test accounts...'); const accounts = await deployInitialSandboxAccounts(pxe); return { - l1Contracts, + aztecNodeConfig, pxe, node, stop, @@ -60,7 +70,7 @@ async function main() { startHttpRpcServer(pxe, createPXERpcServer, PXE_PORT); logger.info(`PXE JSON-RPC Server listening on port ${PXE_PORT}`); logger.info(`Debug logs will be written to ${logPath}`); - const accountStrings = [`Initial Accounts:\n\n`]; + const accountStrings = accounts.length ? [`Initial Accounts:\n\n`] : []; const registeredAccounts = await pxe.getRegisteredAccounts(); for (const account of accounts) { diff --git a/yarn-project/aztec-sandbox/src/sandbox.ts b/yarn-project/aztec-sandbox/src/sandbox.ts index 81ba9429050..2e2d6b0c9f4 100644 --- a/yarn-project/aztec-sandbox/src/sandbox.ts +++ b/yarn-project/aztec-sandbox/src/sandbox.ts @@ -3,6 +3,7 @@ import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/azte import { DeployL1Contracts, L1ContractArtifactsForDeployment, + NULL_KEY, createEthereumChain, deployL1Contracts, } from '@aztec/ethereum'; @@ -22,7 +23,7 @@ import { } from '@aztec/l1-artifacts'; import { createPXEService, getPXEServiceConfig } from '@aztec/pxe'; -import { createPublicClient, http as httpViemTransport } from 'viem'; +import { HDAccount, createPublicClient, http as httpViemTransport } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; @@ -65,23 +66,12 @@ async function waitThenDeploy(config: AztecNodeConfig, deployFunction: () => Pro return await deployFunction(); } -/** Sandbox settings. */ -export type SandboxConfig = AztecNodeConfig & { - /** Mnemonic used to derive the L1 deployer private key.*/ - l1Mnemonic: string; -}; - /** - * Create and start a new Aztec Node and PXE. Deploys L1 contracts. - * Does not start any HTTP services nor populate any initial accounts. - * @param config - Optional Sandbox settings. + * Function to deploy our L1 contracts to the sandbox L1 + * @param aztecNodeConfig - The Aztec Node Config + * @param hdAccount - Account for publishing L1 contracts */ -export async function createSandbox(config: Partial = {}) { - const aztecNodeConfig: AztecNodeConfig = { ...getConfigEnvVars(), ...config }; - const pxeServiceConfig = getPXEServiceConfig(); - const hdAccount = mnemonicToAccount(config.l1Mnemonic ?? MNEMONIC); - const privKey = hdAccount.getHdKey().privateKey; - +async function deployContractsToL1(aztecNodeConfig: AztecNodeConfig, hdAccount: HDAccount) { const l1Artifacts: L1ContractArtifactsForDeployment = { contractDeploymentEmitter: { contractAbi: ContractDeploymentEmitterAbi, @@ -108,12 +98,37 @@ export async function createSandbox(config: Partial = {}) { const l1Contracts = await waitThenDeploy(aztecNodeConfig, () => deployL1Contracts(aztecNodeConfig.rpcUrl, hdAccount, localAnvil, logger, l1Artifacts), ); - aztecNodeConfig.publisherPrivateKey = `0x${Buffer.from(privKey!).toString('hex')}`; aztecNodeConfig.l1Contracts.rollupAddress = l1Contracts.l1ContractAddresses.rollupAddress; aztecNodeConfig.l1Contracts.contractDeploymentEmitterAddress = l1Contracts.l1ContractAddresses.contractDeploymentEmitterAddress; aztecNodeConfig.l1Contracts.inboxAddress = l1Contracts.l1ContractAddresses.inboxAddress; aztecNodeConfig.l1Contracts.registryAddress = l1Contracts.l1ContractAddresses.registryAddress; + return l1Contracts; +} + +/** Sandbox settings. */ +export type SandboxConfig = AztecNodeConfig & { + /** Mnemonic used to derive the L1 deployer private key.*/ + l1Mnemonic: string; +}; + +/** + * Create and start a new Aztec Node and PXE. Deploys L1 contracts. + * Does not start any HTTP services nor populate any initial accounts. + * @param config - Optional Sandbox settings. + */ +export async function createSandbox(config: Partial = {}) { + const aztecNodeConfig: AztecNodeConfig = { ...getConfigEnvVars(), ...config }; + const pxeServiceConfig = getPXEServiceConfig(); + const hdAccount = mnemonicToAccount(config.l1Mnemonic ?? MNEMONIC); + if (aztecNodeConfig.publisherPrivateKey === NULL_KEY) { + const privKey = hdAccount.getHdKey().privateKey; + aztecNodeConfig.publisherPrivateKey = `0x${Buffer.from(privKey!).toString('hex')}`; + } + + if (!aztecNodeConfig.p2pEnabled) { + await deployContractsToL1(aztecNodeConfig, hdAccount); + } const node = await AztecNodeService.createAndSync(aztecNodeConfig); const pxe = await createPXEService(node, pxeServiceConfig); @@ -123,5 +138,5 @@ export async function createSandbox(config: Partial = {}) { await node.stop(); }; - return { node, pxe, l1Contracts, stop }; + return { node, pxe, aztecNodeConfig, stop }; } diff --git a/yarn-project/aztec.js/src/contract_deployer/deploy_method.ts b/yarn-project/aztec.js/src/contract_deployer/deploy_method.ts index f6e1991e632..a97ae9fc6cb 100644 --- a/yarn-project/aztec.js/src/contract_deployer/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract_deployer/deploy_method.ts @@ -123,7 +123,7 @@ export class DeployMethod extends Bas */ public send(options: DeployOptions = {}): DeploySentTx { const txHashPromise = super.send(options).getTxHash(); - return new DeploySentTx(this.artifact, this.pxe, txHashPromise); + return new DeploySentTx(this.artifact, this.pxe, txHashPromise, this.completeAddress); } /** diff --git a/yarn-project/aztec.js/src/contract_deployer/deploy_sent_tx.ts b/yarn-project/aztec.js/src/contract_deployer/deploy_sent_tx.ts index 4441c831bed..93c16ad56a9 100644 --- a/yarn-project/aztec.js/src/contract_deployer/deploy_sent_tx.ts +++ b/yarn-project/aztec.js/src/contract_deployer/deploy_sent_tx.ts @@ -1,4 +1,4 @@ -import { FieldsOf } from '@aztec/circuits.js'; +import { CompleteAddress, FieldsOf } from '@aztec/circuits.js'; import { ContractArtifact } from '@aztec/foundation/abi'; import { TxHash, TxReceipt } from '@aztec/types'; @@ -20,7 +20,16 @@ export type DeployTxReceipt = FieldsO * A contract deployment transaction sent to the network, extending SentTx with methods to create a contract instance. */ export class DeploySentTx extends SentTx { - constructor(private artifact: ContractArtifact, wallet: PXE | Wallet, txHashPromise: Promise) { + constructor( + private artifact: ContractArtifact, + wallet: PXE | Wallet, + txHashPromise: Promise, + + /** + * The complete address of the deployed contract + */ + public completeContractAddress?: CompleteAddress, + ) { super(wallet, txHashPromise); } diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 6ac81cd4af2..3b164760ddf 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -225,7 +225,8 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { const deployed = await tx.wait(); log(`\nContract deployed at ${deployed.contractAddress!.toString()}\n`); } else { - log(`\nDeployment transaction hash: ${txHash}\n`); + log(`\nContract Address: ${tx.completeContractAddress?.address.toString() ?? 'N/A'}`); + log(`Deployment transaction hash: ${txHash}\n`); } }); @@ -458,7 +459,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { const wallet = await getSchnorrAccount(client, privateKey, privateKey, accountCreationSalt).getWallet(); const contract = await Contract.at(contractAddress, contractArtifact, wallet); const tx = contract.methods[functionName](...functionArgs).send(); - log(`Transaction hash: ${(await tx.getTxHash()).toString()}`); + log(`\nTransaction hash: ${(await tx.getTxHash()).toString()}`); if (options.wait) { await tx.wait(); @@ -469,7 +470,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { log(`Block number: ${receipt.blockNumber}`); log(`Block hash: ${receipt.blockHash?.toString('hex')}`); } else { - log('\nTransaction pending. Check status with get-tx-receipt'); + log('Transaction pending. Check status with get-tx-receipt'); } }); diff --git a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts index 5875d3af99a..5e66d1222c6 100644 --- a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts @@ -13,6 +13,7 @@ import { range, } from '@aztec/circuits.js'; import { fr, makeNewContractData, makeProof } from '@aztec/circuits.js/factories'; +import { createEthereumChain } from '@aztec/ethereum'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { to2Fields } from '@aztec/foundation/serialize'; @@ -87,6 +88,8 @@ describe('L1Publisher integration', () => { // The global variables of the last rollup let prevGlobals: GlobalVariables; + const chainId = createEthereumChain(config.rpcUrl, config.apiKey).chainInfo.id; + beforeEach(async () => { deployerAccount = privateKeyToAccount(deployerPK); const { @@ -148,7 +151,7 @@ describe('L1Publisher integration', () => { const historicTreeRoots = await getHistoricBlockData(builderDb, prevGlobals); const tx = await makeEmptyProcessedTxFromHistoricTreeRoots( historicTreeRoots, - new Fr(config.chainId), + new Fr(chainId), new Fr(config.version), ); return tx; @@ -157,7 +160,7 @@ describe('L1Publisher integration', () => { const makeBloatedProcessedTx = async (seed = 0x1) => { const tx = mockTx(seed); const kernelOutput = KernelCircuitPublicInputs.empty(); - kernelOutput.constants.txContext.chainId = fr(config.chainId); + kernelOutput.constants.txContext.chainId = fr(chainId); kernelOutput.constants.txContext.version = fr(config.version); kernelOutput.constants.blockData = await getHistoricBlockData(builderDb, prevGlobals); kernelOutput.end.publicDataUpdateRequests = makeTuple( @@ -267,7 +270,7 @@ describe('L1Publisher integration', () => { await makeBloatedProcessedTx(totalNullifiersPerBlock * i + 4 * MAX_NEW_NULLIFIERS_PER_TX), ]; const globalVariables = new GlobalVariables( - new Fr(config.chainId), + new Fr(chainId), new Fr(config.version), new Fr(1 + i), new Fr(await rollup.read.lastBlockTs()), @@ -359,7 +362,7 @@ describe('L1Publisher integration', () => { await makeEmptyProcessedTx(), ]; const globalVariables = new GlobalVariables( - new Fr(config.chainId), + new Fr(chainId), new Fr(config.version), new Fr(1 + i), new Fr(await rollup.read.lastBlockTs()), diff --git a/yarn-project/ethereum/src/constants.ts b/yarn-project/ethereum/src/constants.ts new file mode 100644 index 00000000000..fa71f161b2a --- /dev/null +++ b/yarn-project/ethereum/src/constants.ts @@ -0,0 +1,3 @@ +import { Hex } from 'viem'; + +export const NULL_KEY: Hex = `0x${'0000000000000000000000000000000000000000000000000000000000000000'}`; diff --git a/yarn-project/ethereum/src/index.ts b/yarn-project/ethereum/src/index.ts index c8edddb8dde..6765234a136 100644 --- a/yarn-project/ethereum/src/index.ts +++ b/yarn-project/ethereum/src/index.ts @@ -6,6 +6,7 @@ import { createTestnetChain } from './testnet.js'; export * from './testnet.js'; export * from './deploy_l1_contracts.js'; export * from './l1_contract_addresses.js'; +export * from './constants.js'; /** * Helper function to create an instance of Aztec Chain from an rpc url and api key. diff --git a/yarn-project/p2p-bootstrap/terraform/main.tf b/yarn-project/p2p-bootstrap/terraform/main.tf index cd3e4d591c1..8e44f28913d 100644 --- a/yarn-project/p2p-bootstrap/terraform/main.tf +++ b/yarn-project/p2p-bootstrap/terraform/main.tf @@ -97,7 +97,7 @@ resource "aws_ecs_task_definition" "aztec-bootstrap-1" { [ { "name": "${var.DEPLOY_TAG}-aztec-bootstrap-1", - "image": "${var.ECR_URL}/p2p-bootstrap:latest", + "image": "${var.ECR_URL}/p2p-bootstrap:aztec3-packages-prod", "essential": true, "command": ["start"], "memoryReservation": 3776, @@ -273,7 +273,7 @@ resource "aws_ecs_task_definition" "aztec-bootstrap-2" { [ { "name": "${var.DEPLOY_TAG}-aztec-bootstrap-2", - "image": "${var.ECR_URL}/p2p-bootstrap:latest", + "image": "${var.ECR_URL}/p2p-bootstrap:aztec3-packages-prod", "essential": true, "command": ["start"], "memoryReservation": 3776, diff --git a/yarn-project/sequencer-client/src/config.ts b/yarn-project/sequencer-client/src/config.ts index 89e884484e6..1db3bdf6c3c 100644 --- a/yarn-project/sequencer-client/src/config.ts +++ b/yarn-project/sequencer-client/src/config.ts @@ -1,6 +1,8 @@ -import { L1ContractAddresses } from '@aztec/ethereum'; +import { L1ContractAddresses, NULL_KEY } from '@aztec/ethereum'; import { EthAddress } from '@aztec/foundation/eth-address'; +import { Hex } from 'viem'; + import { GlobalReaderConfig } from './global_variable_builder/index.js'; import { PublisherConfig, TxSenderConfig } from './publisher/config.js'; import { SequencerConfig } from './sequencer/config.js'; @@ -43,11 +45,9 @@ export function getConfigEnvVars(): SequencerClientConfig { CONTRACT_DEPLOYMENT_EMITTER_ADDRESS, } = process.env; - const publisherPrivateKey: `0x${string}` = `0x${ - SEQ_PUBLISHER_PRIVATE_KEY - ? SEQ_PUBLISHER_PRIVATE_KEY.replace('0x', '') - : '0000000000000000000000000000000000000000000000000000000000000000' - }`; + const publisherPrivateKey: Hex = SEQ_PUBLISHER_PRIVATE_KEY + ? `0x${SEQ_PUBLISHER_PRIVATE_KEY.replace('0x', '')}` + : NULL_KEY; // Populate the relevant addresses for use by the sequencer const addresses: L1ContractAddresses = { rollupAddress: ROLLUP_CONTRACT_ADDRESS ? EthAddress.fromString(ROLLUP_CONTRACT_ADDRESS) : EthAddress.ZERO, diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 5966ca6c1bf..f826b1112ef 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -44,7 +44,7 @@ describe('sequencer', () => { let sequencer: TestSubject; - const chainId = Fr.ZERO; + const chainId = new Fr(12345); const version = Fr.ZERO; beforeEach(() => { @@ -99,6 +99,7 @@ describe('sequencer', () => { it('builds a block out of a single tx', async () => { const tx = mockTx(); + tx.data.constants.txContext.chainId = chainId; const block = L2Block.random(lastBlockNumber + 1); const proof = makeEmptyProof(); @@ -124,6 +125,9 @@ describe('sequencer', () => { it('builds a block out of several txs rejecting double spends', async () => { const txs = [mockTx(0x10000), mockTx(0x20000), mockTx(0x30000)]; + txs.forEach(tx => { + tx.data.constants.txContext.chainId = chainId; + }); const doubleSpendTx = txs[1]; const block = L2Block.random(lastBlockNumber + 1); const proof = makeEmptyProof(); @@ -156,6 +160,39 @@ describe('sequencer', () => { expect(publisher.processL2Block).toHaveBeenCalledWith(block); expect(p2p.deleteTxs).toHaveBeenCalledWith([await doubleSpendTx.getTxHash()]); }); + + it('builds a block out of several txs rejecting incorrect chain ids', async () => { + const txs = [mockTx(0x10000), mockTx(0x20000), mockTx(0x30000)]; + txs.forEach(tx => { + tx.data.constants.txContext.chainId = chainId; + }); + const invalidChainTx = txs[1]; + const block = L2Block.random(lastBlockNumber + 1); + const proof = makeEmptyProof(); + + p2p.getTxs.mockResolvedValueOnce(txs); + blockBuilder.buildL2Block.mockResolvedValueOnce([block, proof]); + publisher.processL2Block.mockResolvedValueOnce(true); + globalVariableBuilder.buildGlobalVariables.mockResolvedValueOnce( + new GlobalVariables(chainId, version, new Fr(lastBlockNumber + 1), Fr.ZERO), + ); + + // We make the chain id on the invalid tx not equal to the configured chain id + invalidChainTx.data.constants.txContext.chainId = new Fr(1n + chainId.value); + + await sequencer.initialSync(); + await sequencer.work(); + + const expectedTxHashes = [...(await Tx.getHashes([txs[0], txs[2]])), TxHash.ZERO, TxHash.ZERO]; + + expect(blockBuilder.buildL2Block).toHaveBeenCalledWith( + new GlobalVariables(chainId, version, new Fr(lastBlockNumber + 1), Fr.ZERO), + expectedTxHashes.map(hash => expect.objectContaining({ hash })), + Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n)), + ); + expect(publisher.processL2Block).toHaveBeenCalledWith(block); + expect(p2p.deleteTxs).toHaveBeenCalledWith([await invalidChainTx.getTxHash()]); + }); }); class TestSubject extends Sequencer { diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index f300549024b..c0368c02434 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -130,17 +130,17 @@ export class Sequencer { if (pendingTxs.length < this.minTxsPerBLock) return; this.log.info(`Retrieved ${pendingTxs.length} txs from P2P pool`); + const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1; + const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(new Fr(blockNumber)); + // Filter out invalid txs // TODO: It should be responsibility of the P2P layer to validate txs before passing them on here - const validTxs = await this.takeValidTxs(pendingTxs); + const validTxs = await this.takeValidTxs(pendingTxs, newGlobalVariables); if (validTxs.length < this.minTxsPerBLock) return; - const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1; - this.log.info(`Building block ${blockNumber} with ${validTxs.length} transactions`); this.state = SequencerState.CREATING_BLOCK; - const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(new Fr(blockNumber)); const prevGlobalVariables = (await this.l2BlockSource.getBlock(-1))?.globalVariables ?? GlobalVariables.empty(); // Process txs and drop the ones that fail processing @@ -156,8 +156,8 @@ export class Sequencer { // Only accept processed transactions that are not double-spends, // public functions emitting nullifiers would pass earlier check but fail here. // Note that we're checking all nullifiers generated in the private execution twice, - // we could store the ones already checked and skip them here as an optimization. - const processedValidTxs = await this.takeValidTxs(processedTxs); + // we could store the ones already checked and skip them here as an optimisation. + const processedValidTxs = await this.takeValidTxs(processedTxs, newGlobalVariables); if (processedValidTxs.length === 0) { this.log('No txs processed correctly to build block. Exiting'); @@ -240,16 +240,25 @@ export class Sequencer { } } - protected async takeValidTxs(txs: T[]): Promise { + protected async takeValidTxs(txs: T[], globalVariables: GlobalVariables): Promise { const validTxs: T[] = []; - const doubleSpendTxs = []; + const txsToDelete = []; const thisBlockNullifiers: Set = new Set(); // Process txs until we get to maxTxsPerBlock, rejecting double spends in the process for (const tx of txs) { + if (tx.data.constants.txContext.chainId.value !== globalVariables.chainId.value) { + this.log( + `Deleting tx for incorrect chain ${tx.data.constants.txContext.chainId.toString()}, tx hash ${await Tx.getHash( + tx, + )}`, + ); + txsToDelete.push(tx); + continue; + } if (await this.isTxDoubleSpend(tx)) { this.log(`Deleting double spend tx ${await Tx.getHash(tx)}`); - doubleSpendTxs.push(tx); + txsToDelete.push(tx); continue; } else if (this.isTxDoubleSpendSameBlock(tx, thisBlockNullifiers)) { // We don't drop these txs from the p2p pool immediately since they become valid @@ -264,8 +273,8 @@ export class Sequencer { } // Make sure we remove these from the tx pool so we do not consider it again - if (doubleSpendTxs.length > 0) { - await this.p2pClient.deleteTxs(await Tx.getHashes([...doubleSpendTxs])); + if (txsToDelete.length > 0) { + await this.p2pClient.deleteTxs(await Tx.getHashes([...txsToDelete])); } return validTxs;