Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion spartan/environments/prove-n-tps-real.env
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
100 changes: 66 additions & 34 deletions yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -122,7 +122,7 @@ describe(`prove ${TARGET_TPS}TPS test`, () => {
let producerPromises: Promise<void>[];

let aztecNode: AztecNode;
let benchmarkContract: BenchmarkingContract;
let benchmarkContract: AvmGadgetsTestContract;

let metrics: ProvingMetrics;
let childProcesses: ChildProcess[];
Expand Down Expand Up @@ -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);
});
Expand All @@ -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 },
});
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
}

Expand All @@ -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([
Expand 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
Expand All @@ -530,7 +562,7 @@ describe(`prove ${TARGET_TPS}TPS test`, () => {
async function createTx(
wallet: WorkerWallet,
accountAddress: AztecAddress,
benchmarkContract: BenchmarkingContract,
benchmarkContract: AvmGadgetsTestContract,
logger: Logger,
): Promise<Tx> {
logger.info('Creating prototype transaction...');
Expand All @@ -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');
Expand Down Expand Up @@ -579,7 +611,7 @@ async function cloneTx(tx: Tx, aztecNode: AztecNode): Promise<Tx> {

async function startProducing(
producer: WalletTxProducer,
benchmarkContract: BenchmarkingContract,
benchmarkContract: AvmGadgetsTestContract,
aztecNode: AztecNode,
signal: AbortSignal,
logger: Logger,
Expand Down
Loading