diff --git a/spartan/environments/prove-n-tps-real.env b/spartan/environments/prove-n-tps-real.env index 55e5bd30fab0..4be76065b0ca 100644 --- a/spartan/environments/prove-n-tps-real.env +++ b/spartan/environments/prove-n-tps-real.env @@ -36,8 +36,11 @@ PROVER_PUBLISHER_MNEMONIC_START_INDEX=8000 PROVER_AGENT_POLL_INTERVAL_MS=10000 PUBLISHERS_PER_PROVER=1 -SEQ_MAX_TX_PER_BLOCK=80 +SEQ_MAX_TX_PER_BLOCK=18 SEQ_MIN_TX_PER_BLOCK=0 +SEQ_BLOCK_DURATION_MS=6000 +SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT=36 +SEQ_BUILD_CHECKPOINT_IF_EMPTY=true P2P_MAX_TX_POOL_SIZE=1000000000 DEBUG_P2P_INSTRUMENT_MESSAGES=true diff --git a/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts b/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts index 5c1750c41b24..0e20ccefc471 100644 --- a/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts +++ b/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts @@ -8,13 +8,13 @@ import { RollupCheatCodes } from '@aztec/aztec/testing'; import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants'; import { EthCheatCodesWithState } from '@aztec/ethereum/test'; import { SlotNumber } from '@aztec/foundation/branded-types'; -import { timesAsync } from '@aztec/foundation/collection'; +import { timesParallel } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { retryUntil } from '@aztec/foundation/retry'; import { sleep } from '@aztec/foundation/sleep'; -import { DateProvider } from '@aztec/foundation/timer'; -import { BenchmarkingContract } from '@aztec/noir-test-contracts.js/Benchmarking'; +import { DateProvider, Timer } from '@aztec/foundation/timer'; +import { AvmGadgetsTestContract } from '@aztec/noir-test-contracts.js/AvmGadgetsTest'; import { GasFees } from '@aztec/stdlib/gas'; import { deriveSigningKey } from '@aztec/stdlib/keys'; import { Tx, TxHash } from '@aztec/stdlib/tx'; @@ -122,7 +122,7 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { let producerPromises: Promise[]; let aztecNode: AztecNode; - let benchmarkContract: BenchmarkingContract; + let benchmarkContract: AvmGadgetsTestContract; let metrics: ProvingMetrics; let childProcesses: ChildProcess[]; @@ -269,7 +269,7 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { ); logger.info(`Creating ${NUM_WALLETS} wallet(s)...`); - testWallets = await timesAsync(NUM_WALLETS, i => { + testWallets = await timesParallel(NUM_WALLETS, i => { logger.info(`Creating wallet ${i + 1}/${NUM_WALLETS}`); return createWorkerWalletClient(rpcUrl, config.REAL_VERIFIER, logger); }); @@ -278,33 +278,31 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { // Register FPC and create/deploy accounts const fpcAddress = await getSponsoredFPCAddress(); const sponsor = new SponsoredFeePaymentMethod(fpcAddress); - accountAddresses = []; - for (const wallet of wallets) { - const secret = Fr.random(); - const salt = Fr.random(); - // Register account inside worker (populates TestWallet.accounts map) - const address = await wallet.registerAccount(secret, salt); - // Register FPC in worker's PXE - await registerSponsoredFPC(wallet); - // Deploy via standard AccountManager flow (from: ZERO -> SignerlessAccount, no account lookup) - const manager = await AccountManager.create( - wallet, - secret, - new SchnorrAccountContract(deriveSigningKey(secret)), - salt, - ); - const deployMethod = await manager.getDeployMethod(); - await deployMethod.send({ - from: AztecAddress.ZERO, - fee: { paymentMethod: sponsor }, - wait: { timeout: 2400 }, - }); - logger.info(`Account deployed at ${address}`); - accountAddresses.push(address); - } + accountAddresses = await Promise.all( + wallets.map(async wallet => { + const secret = Fr.random(); + const salt = Fr.random(); + const address = await wallet.registerAccount(secret, salt); + await registerSponsoredFPC(wallet); + const manager = await AccountManager.create( + wallet, + secret, + new SchnorrAccountContract(deriveSigningKey(secret)), + salt, + ); + const deployMethod = await manager.getDeployMethod(); + await deployMethod.send({ + from: AztecAddress.ZERO, + fee: { paymentMethod: sponsor }, + wait: { timeout: 2400 }, + }); + logger.info(`Account deployed at ${address}`); + return address; + }), + ); logger.info('Deploying benchmark contract...'); - benchmarkContract = await BenchmarkingContract.deploy(wallets[0]).send({ + benchmarkContract = await AvmGadgetsTestContract.deploy(wallets[0]).send({ from: accountAddresses[0], fee: { paymentMethod: sponsor }, }); @@ -365,6 +363,14 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { await scaleProverAgents(config.NAMESPACE, 10, logger); }); + afterAll(async () => { + try { + await scaleProverAgents(config.NAMESPACE, 2, logger); + } catch (err) { + logger.error(`Failed to scale prover agents: ${err}`); + } + }); + it(`sends ${TARGET_TPS} TPS for a full epoch and waits for proof`, async () => { const [testEpoch, startSlot, { proven: startProvenBlockNumber, pending: startBlockNumber }] = await Promise.all([ rollupCheatCodes.getEpoch(), @@ -432,6 +438,8 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { let failureCount = 0; const batchSize = 10; + const TX_MINING_TIMEOUT_S = epochDurationSeconds; + const miningTimer = new Timer(); while (pendingTxs.size > 0) { const entries = [...pendingTxs.entries()]; const start = Math.floor(Math.random() * Math.max(1, entries.length - batchSize + 1)); @@ -464,6 +472,22 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { ); } + if (miningTimer.s() > TX_MINING_TIMEOUT_S) { + const remainingHashes = [...pendingTxs.values()].map(h => h.toString()); + logger.warn( + `Timed out waiting for ${pendingTxs.size}/${totalSent} transactions after ${TX_MINING_TIMEOUT_S}s. ` + + `These transactions likely were not included in this epoch's blocks. ` + + `Remaining tx hashes: ${remainingHashes.join(', ')}`, + ); + break; + } + + if (processedCount === 0) { + logger.info( + `Still waiting for ${pendingTxs.size}/${totalSent} transactions (${Math.floor(miningTimer.s())}s elapsed)`, + ); + } + await sleep(500); } @@ -483,6 +507,8 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { // Poll for proof completion while detecting reorgs let lastBlockNumber = endBlockNumber; + const PROOF_TIMEOUT_S = epochDurationSeconds; + const proofTimer = new Timer(); while (true) { const [provenBlock, currentBlockNumber] = await Promise.all([ @@ -507,7 +533,13 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { break; } - logger.debug(`Proven: ${provenBlock}, Pending: ${currentBlockNumber}, Target: ${targetProvenBlock}`); + if (proofTimer.s() > PROOF_TIMEOUT_S) { + throw new Error( + `Timed out waiting for proof after ${PROOF_TIMEOUT_S}s. Proven: ${provenBlock}, Target: ${targetProvenBlock}`, + ); + } + + logger.info(`Proven: ${provenBlock}, Pending: ${currentBlockNumber}, Target: ${targetProvenBlock}`); lastBlockNumber = currentBlockNumber; await sleep(10 * 1000); // Poll every 10 seconds @@ -530,7 +562,7 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { async function createTx( wallet: WorkerWallet, accountAddress: AztecAddress, - benchmarkContract: BenchmarkingContract, + benchmarkContract: AvmGadgetsTestContract, logger: Logger, ): Promise { logger.info('Creating prototype transaction...'); @@ -539,7 +571,7 @@ async function createTx( from: accountAddress, fee: { paymentMethod: sponsor, gasSettings: { maxPriorityFeesPerGas: GasFees.empty() } }, }; - const interaction = benchmarkContract.methods.sha256_hash_1024(Array(1024).fill(42)); + const interaction = benchmarkContract.methods.keccak_hash_1400(Array(1400).fill(42)); const execPayload = await interaction.request(options); const tx = await wallet.proveTx(execPayload, toSendOptions(options)); logger.info('Prototype transaction created'); @@ -579,7 +611,7 @@ async function cloneTx(tx: Tx, aztecNode: AztecNode): Promise { async function startProducing( producer: WalletTxProducer, - benchmarkContract: BenchmarkingContract, + benchmarkContract: AvmGadgetsTestContract, aztecNode: AztecNode, signal: AbortSignal, logger: Logger,