|
| 1 | +import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; |
| 2 | +import { EpochNumber } from '@aztec/foundation/branded-types'; |
| 3 | +import { timesAsync } from '@aztec/foundation/collection'; |
| 4 | +import { Fr } from '@aztec/foundation/curves/bn254'; |
| 5 | +import { EthAddress } from '@aztec/foundation/eth-address'; |
| 6 | +import { type Logger, createLogger } from '@aztec/foundation/log'; |
| 7 | +import { getTestData, isGenerateTestDataEnabled } from '@aztec/foundation/testing'; |
| 8 | +import { updateProtocolCircuitSampleInputs } from '@aztec/foundation/testing/files'; |
| 9 | +import type { CircuitName } from '@aztec/stdlib/stats'; |
| 10 | + |
| 11 | +import TOML from '@iarna/toml'; |
| 12 | + |
| 13 | +import { TestContext, makeTestDeferredJobQueue } from '../mocks/test_context.js'; |
| 14 | +import { CheckpointSubTreeOrchestrator } from '../orchestrator/checkpoint-sub-tree-orchestrator.js'; |
| 15 | +import { ChonkCache } from '../orchestrator/chonk-cache.js'; |
| 16 | +import { type CheckpointTopTreeData, TopTreeOrchestrator } from '../orchestrator/top-tree-orchestrator.js'; |
| 17 | + |
| 18 | +// Regenerates the committed `crates/rollup-*/Prover.toml` sample inputs that CI runs `nargo execute` |
| 19 | +// against. The rollup circuits push their serialized inputs via `pushTestData` whenever they run |
| 20 | +// through the prover, so driving representative epochs through the (simulated) orchestrator and then |
| 21 | +// dumping `getTestData(circuitName)` produces fresh, ABI-current fixtures. Run with: |
| 22 | +// AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test regenerate_rollup_sample_inputs |
| 23 | +// Without that flag the whole suite is skipped, so it is a no-op (no prover setup) in normal CI. |
| 24 | +// |
| 25 | +// The four scenarios are chosen to exercise each block-root variant the orchestrator selects (see |
| 26 | +// BlockProvingState#getBlockRootRollupTypeAndInputs): a first block with 0 txs, with >=2 txs, a |
| 27 | +// three-block checkpoint (first block with 1 tx plus a block-merge), and a three-checkpoint epoch |
| 28 | +// (for the checkpoint-merge). A merge node only exists above the tree root, so both merges need |
| 29 | +// three leaves — two would pair directly at the root. Every scenario also produces the root rollup. |
| 30 | +const describeOrSkip = isGenerateTestDataEnabled() ? describe : describe.skip; |
| 31 | + |
| 32 | +describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { |
| 33 | + let context: TestContext; |
| 34 | + let log: Logger; |
| 35 | + |
| 36 | + interface Scenario { |
| 37 | + numCheckpoints: number; |
| 38 | + numBlocksPerCheckpoint: number; |
| 39 | + numTxsPerBlock: number; |
| 40 | + numL1ToL2Messages: number; |
| 41 | + /** Circuits whose sample inputs this scenario is responsible for regenerating. */ |
| 42 | + dump: CircuitName[]; |
| 43 | + } |
| 44 | + |
| 45 | + const withMessages = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP; |
| 46 | + |
| 47 | + const scenarios: Scenario[] = [ |
| 48 | + { |
| 49 | + numCheckpoints: 1, |
| 50 | + numBlocksPerCheckpoint: 1, |
| 51 | + numTxsPerBlock: 0, |
| 52 | + numL1ToL2Messages: withMessages, |
| 53 | + dump: ['rollup-block-root-first-empty-tx'], |
| 54 | + }, |
| 55 | + { |
| 56 | + numCheckpoints: 1, |
| 57 | + numBlocksPerCheckpoint: 1, |
| 58 | + numTxsPerBlock: 2, |
| 59 | + numL1ToL2Messages: withMessages, |
| 60 | + dump: ['rollup-block-root-first', 'rollup-root'], |
| 61 | + }, |
| 62 | + { |
| 63 | + numCheckpoints: 1, |
| 64 | + numBlocksPerCheckpoint: 3, |
| 65 | + numTxsPerBlock: 1, |
| 66 | + numL1ToL2Messages: withMessages, |
| 67 | + dump: ['rollup-block-root-first-single-tx', 'rollup-block-merge'], |
| 68 | + }, |
| 69 | + // The checkpoint-merge only appears with three checkpoints. Independently-built checkpoints do |
| 70 | + // not carry the inbox message state forward, so this scenario runs with no L1-to-L2 messages and |
| 71 | + // a zero previous rolling hash, matching the multi-checkpoint case in top-tree-orchestrator.test. |
| 72 | + { |
| 73 | + numCheckpoints: 3, |
| 74 | + numBlocksPerCheckpoint: 1, |
| 75 | + numTxsPerBlock: 1, |
| 76 | + numL1ToL2Messages: 0, |
| 77 | + dump: ['rollup-checkpoint-merge'], |
| 78 | + }, |
| 79 | + ]; |
| 80 | + |
| 81 | + beforeEach(async () => { |
| 82 | + log = createLogger('prover-client:test:regenerate-rollup-sample-inputs'); |
| 83 | + context = await TestContext.new(log, { proverCount: 1 }); |
| 84 | + }); |
| 85 | + |
| 86 | + afterEach(async () => { |
| 87 | + await context.cleanup(); |
| 88 | + }); |
| 89 | + |
| 90 | + it.each(scenarios)( |
| 91 | + 'regenerates $dump from an epoch with $numCheckpoints checkpoints, $numBlocksPerCheckpoint blocks, $numTxsPerBlock txs', |
| 92 | + async ({ numCheckpoints, numBlocksPerCheckpoint, numTxsPerBlock, numL1ToL2Messages, dump }) => { |
| 93 | + const checkpoints = await timesAsync(numCheckpoints, () => |
| 94 | + context.makeCheckpoint(numBlocksPerCheckpoint, { |
| 95 | + numTxsPerBlock, |
| 96 | + numL1ToL2Messages, |
| 97 | + makeProcessedTxOpts: (_, txIndex) => ({ privateOnly: txIndex % 2 === 0 }), |
| 98 | + }), |
| 99 | + ); |
| 100 | + |
| 101 | + const finalBlobChallenges = await context.getFinalBlobChallenges(); |
| 102 | + const chonkCache = new ChonkCache(); |
| 103 | + const subTrees: CheckpointSubTreeOrchestrator[] = []; |
| 104 | + const topTreeData: CheckpointTopTreeData[] = []; |
| 105 | + |
| 106 | + try { |
| 107 | + for (let checkpointIndex = 0; checkpointIndex < numCheckpoints; checkpointIndex++) { |
| 108 | + const { constants, blocks, l1ToL2Messages, previousBlockHeader, checkpoint } = checkpoints[checkpointIndex]; |
| 109 | + |
| 110 | + // First checkpoint starts from genesis; the multi-checkpoint scenario carries no messages, |
| 111 | + // so every checkpoint's previous rolling hash is zero. |
| 112 | + const previousInboxRollingHash = Fr.ZERO; |
| 113 | + |
| 114 | + const subTree = await CheckpointSubTreeOrchestrator.start( |
| 115 | + context.worldState, |
| 116 | + context.prover, |
| 117 | + EthAddress.ZERO, |
| 118 | + chonkCache, |
| 119 | + EpochNumber(1), |
| 120 | + /* cancelJobsOnStop */ false, |
| 121 | + makeTestDeferredJobQueue(), |
| 122 | + constants, |
| 123 | + l1ToL2Messages, |
| 124 | + previousInboxRollingHash, |
| 125 | + numBlocksPerCheckpoint, |
| 126 | + previousBlockHeader, |
| 127 | + ); |
| 128 | + subTrees.push(subTree); |
| 129 | + |
| 130 | + for (let i = 0; i < numBlocksPerCheckpoint; i++) { |
| 131 | + const { header, txs } = blocks[i]; |
| 132 | + const { blockNumber, timestamp } = header.globalVariables; |
| 133 | + |
| 134 | + await subTree.startNewBlock(blockNumber, timestamp, txs.length); |
| 135 | + if (txs.length > 0) { |
| 136 | + await subTree.addTxs(txs); |
| 137 | + } |
| 138 | + await subTree.setBlockCompleted(blockNumber, header); |
| 139 | + } |
| 140 | + |
| 141 | + topTreeData.push({ |
| 142 | + blockProofs: subTree.getSubTreeResult().then(r => r.blockProofOutputs), |
| 143 | + l2ToL1MsgsPerBlock: blocks.map(b => b.txs.map(tx => tx.txEffect.l2ToL1Msgs)), |
| 144 | + blobFields: checkpoint.toBlobFields(), |
| 145 | + previousBlockHeader, |
| 146 | + previousArchiveSiblingPath: subTree.getPreviousArchiveSiblingPath(), |
| 147 | + }); |
| 148 | + } |
| 149 | + |
| 150 | + const topTree = new TopTreeOrchestrator(context.prover, EthAddress.ZERO, makeTestDeferredJobQueue()); |
| 151 | + try { |
| 152 | + await topTree.prove(EpochNumber(1), numCheckpoints, finalBlobChallenges, topTreeData); |
| 153 | + } finally { |
| 154 | + await topTree.stop(); |
| 155 | + } |
| 156 | + |
| 157 | + for (const circuitName of dump) { |
| 158 | + const data = getTestData(circuitName); |
| 159 | + if (!data || data.length === 0) { |
| 160 | + throw new Error(`No test data captured for ${circuitName}; scenario does not exercise it.`); |
| 161 | + } |
| 162 | + updateProtocolCircuitSampleInputs(circuitName, TOML.stringify(data[0] as any)); |
| 163 | + log.info(`Regenerated sample inputs for ${circuitName}`); |
| 164 | + } |
| 165 | + } finally { |
| 166 | + await Promise.all(subTrees.map(s => s.stop())); |
| 167 | + } |
| 168 | + }, |
| 169 | + 300_000, |
| 170 | + ); |
| 171 | +}); |
0 commit comments