Skip to content

Commit 5117cc8

Browse files
authored
feat: merge-train/avm (#20604)
BEGIN_COMMIT_OVERRIDE test: fix and unskip block building tests "processes txs until hitting timetable" and "can call public function from different tx in same block as deployed" (#19252) feat: add public bytecode sizes to benchmarks dashboard (#20704) feat(avm): enable proving during fuzzing (#20523) END_COMMIT_OVERRIDE
2 parents 1122e41 + 2401c02 commit 5117cc8

File tree

6 files changed

+108
-19
lines changed

6 files changed

+108
-19
lines changed

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ TxSimulationResult fuzz_prover(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB
221221
bool check_circuit_result = avm_api.check_circuit(proving_inputs);
222222
BB_ASSERT(check_circuit_result,
223223
"check_circuit returned false in fuzzer with no exception, this indicates a failure");
224+
225+
// 6. Prove and verify
226+
auto proof = avm_api.prove(proving_inputs);
227+
bool verified = avm_api.verify(proof, proving_inputs.public_inputs);
228+
BB_ASSERT(verified, "Proof verification failed");
224229
#else
225230
// In coverage builds, run simulate_for_witgen and tracegen instead of check_circuit
226231
// This gives us coverage the the event and tracegen code paths without the overhead of check_circuit

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ SimulatorResult fuzz_tx(bb::avm2::fuzzer::FuzzerWorldStateManager& ws_mgr,
102102
bb::avm2::fuzzer::FuzzerContractDB& contract_db,
103103
FuzzerTxData& tx_data);
104104

105-
// Run the prover fuzzer: fast simulation, hint collection, comparison, and check_circuit
105+
// Run the prover fuzzer: fast simulation, hint collection, comparison, check_circuit, prove, and verify
106106
// Returns simulation result on success, throws if the input should be rejected
107107
TxSimulationResult fuzz_prover(bb::avm2::fuzzer::FuzzerWorldStateManager& ws_mgr,
108108
bb::avm2::fuzzer::FuzzerContractDB& contract_db,

barretenberg/cpp/src/barretenberg/avm_fuzzer/prover.fuzzer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ extern "C" int LLVMFuzzerInitialize(int*, char***)
9999
memset(l2_to_l1_msgs_counter, 0, sizeof(l2_to_l1_msgs_counter));
100100
memset(public_logs_counter, 0, sizeof(public_logs_counter));
101101

102+
bb::srs::init_file_crs_factory(bb::srs::bb_crs_path());
102103
FuzzerWorldStateManager::initialize();
103104
return 0;
104105
}

yarn-project/end-to-end/src/e2e_block_building.test.ts

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import { StatefulTestContract } from '@aztec/noir-test-contracts.js/StatefulTest
1515
import { TestContract } from '@aztec/noir-test-contracts.js/Test';
1616
import type { SequencerClient } from '@aztec/sequencer-client';
1717
import type { TestSequencerClient } from '@aztec/sequencer-client/test';
18+
import { getAllFunctionAbis } from '@aztec/stdlib/abi';
1819
import { getProofSubmissionDeadlineEpoch } from '@aztec/stdlib/epoch-helpers';
20+
import { GasFees } from '@aztec/stdlib/gas';
1921
import { computeSiloedPrivateLogFirstField } from '@aztec/stdlib/hash';
2022
import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
2123
import { TX_ERROR_EXISTING_NULLIFIER } from '@aztec/stdlib/tx';
@@ -72,36 +74,53 @@ describe('e2e_block_building', () => {
7274
});
7375

7476
afterEach(async () => {
75-
await aztecNodeAdmin.setConfig({ minTxsPerBlock: 1 });
77+
await aztecNodeAdmin.setConfig({
78+
fakeProcessingDelayPerTxMs: 0,
79+
minTxsPerBlock: 1,
80+
maxTxsPerBlock: undefined, // reset to default
81+
enforceTimeTable: false, // reset to false (as it is in setup())
82+
});
7683
// Clean up any mocks
7784
jest.restoreAllMocks();
7885
});
7986

8087
afterAll(() => teardown());
8188

8289
it('processes txs until hitting timetable', async () => {
83-
// We send enough txs so they are spread across multiple blocks, but not
84-
// so many so that we don't end up hitting a reorg or timing out the tx wait().
85-
const TX_COUNT = 16;
90+
const DEADLINE_S = 0.5; // half a second of building per block
91+
const DEADLINE_MS = DEADLINE_S * 1000;
92+
const MAX_TXS_FIT_IN_DEADLINE = 5; // via deadline and fake delay, we force this maximum to be true
93+
const FAKE_DELAY_PER_TX_MS = DEADLINE_MS / MAX_TXS_FIT_IN_DEADLINE; // e.g. 100ms if 5 txs per 0.5s
94+
95+
// the minimum number of blocks we want to see
96+
const EXPECTED_BLOCKS = 3;
97+
// choose a tx count should ensure that we use EXPECTED_BLOCKS or more
98+
// Note that we don't need to ensure that last block is _full_
99+
const TX_COUNT = MAX_TXS_FIT_IN_DEADLINE * (EXPECTED_BLOCKS - 1) + 1;
100+
101+
// print out the test parameters
102+
logger.info(`multi-block timetable test parameters:`);
103+
logger.info(` Deadline per block: ${DEADLINE_MS} ms`);
104+
logger.info(` Fake delay per tx: ${FAKE_DELAY_PER_TX_MS} ms`);
105+
logger.info(` Max txs that should fit in deadline: ${MAX_TXS_FIT_IN_DEADLINE}`);
106+
logger.info(` Total txs to send: ${TX_COUNT}`);
107+
logger.info(` Expected minimum blocks: ${EXPECTED_BLOCKS}`);
86108

87109
const contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress });
88110
logger.info(`Deployed stateful test contract at ${contract.address}`);
89111

90-
// We add a delay to every public tx processing
91-
logger.info(`Updating aztec node config`);
112+
// Configure sequencer with a small delay per tx and enforce timetable
92113
await aztecNodeAdmin.setConfig({
93-
fakeProcessingDelayPerTxMs: 300,
114+
fakeProcessingDelayPerTxMs: FAKE_DELAY_PER_TX_MS, // ensure that each tx takes at least this long
94115
minTxsPerBlock: 1,
95-
maxTxsPerBlock: TX_COUNT,
116+
maxTxsPerBlock: TX_COUNT, // intentionally large because we want to flex deadline, not this max
117+
enforceTimeTable: true,
96118
});
97119

98-
// We also cheat the sequencer's timetable so it allocates little time to processing.
99-
// This will leave the sequencer with just a few seconds to build the block, so it shouldn't
100-
// be able to squeeze in more than a few txs in each. This is sensitive to the time it takes
101-
// to pick up and validate the txs, so we may need to bump it to work on CI.
120+
// Mock the timetable to limit time for block building.
102121
jest.spyOn(sequencer.sequencer.timetable, 'canStartNextBlock').mockImplementation((secondsIntoSlot: number) => ({
103122
canStart: true,
104-
deadline: secondsIntoSlot + 1, // Give only 1 second for building
123+
deadline: secondsIntoSlot + DEADLINE_S, // limit block-building time
105124
isLastBlock: true,
106125
}));
107126

@@ -118,7 +137,7 @@ describe('e2e_block_building', () => {
118137
const receipts = await Promise.all(txHashes.map(txHash => waitForTx(aztecNode, txHash)));
119138
const blockNumbers = receipts.map(r => r.blockNumber!).sort((a, b) => a - b);
120139
logger.info(`Txs mined on blocks: ${unique(blockNumbers)}`);
121-
expect(blockNumbers.at(-1)! - blockNumbers[0]).toBeGreaterThan(1);
140+
expect(blockNumbers.at(-1)! - blockNumbers[0]).toBeGreaterThan(EXPECTED_BLOCKS - 1);
122141
});
123142

124143
it('assembles a block with multiple txs', async () => {
@@ -225,7 +244,8 @@ describe('e2e_block_building', () => {
225244
logger.info(`Txs sent`);
226245
});
227246

228-
it.skip('can call public function from different tx in same block as deployed', async () => {
247+
// Uses priority fees to guarantee the deploy tx is ordered before the call tx within the same block.
248+
it('can call public function from different tx in same block as deployed', async () => {
229249
// Ensure both txs will land on the same block
230250
await aztecNodeAdmin.setConfig({ minTxsPerBlock: 2 });
231251

@@ -239,13 +259,29 @@ describe('e2e_block_building', () => {
239259
const callInteraction = new ContractFunctionInteraction(
240260
wallet,
241261
deployerInstance.address,
242-
TokenContract.artifact.functions.find(x => x.name === 'set_minter')!,
262+
getAllFunctionAbis(TokenContract.artifact).find(x => x.name === 'set_minter')!,
243263
[minterAddress, true],
244264
);
245265

266+
// Use priority fees to guarantee ordering: deploy tx gets higher priority so the
267+
// sequencer places it before the call tx in the block.
268+
const highPriority = new GasFees(100, 100);
269+
const lowPriority = new GasFees(1, 1);
270+
271+
const deployTxHash = await deployMethod.send({
272+
from: ownerAddress,
273+
fee: { gasSettings: { maxPriorityFeesPerGas: highPriority } },
274+
wait: NO_WAIT,
275+
});
276+
const callTxHash = await callInteraction.send({
277+
from: ownerAddress,
278+
fee: { gasSettings: { maxPriorityFeesPerGas: lowPriority } },
279+
wait: NO_WAIT,
280+
});
281+
246282
const [deployTxReceipt, callTxReceipt] = await Promise.all([
247-
deployMethod.send({ from: ownerAddress, wait: { returnReceipt: true } }),
248-
callInteraction.send({ from: ownerAddress }),
283+
waitForTx(aztecNode, deployTxHash),
284+
waitForTx(aztecNode, callTxHash),
249285
]);
250286

251287
expect(deployTxReceipt.blockNumber).toEqual(callTxReceipt.blockNumber);

yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
169169
}
170170
const avmResult = await this.simulator.simulate(tx, fullTxLabel);
171171

172+
await this.#recordBytecodeSizes(fullTxLabel, [...setupCalls, ...appCalls, ...(teardownCall ? [teardownCall] : [])]);
173+
172174
// Something like this is often useful for debugging:
173175
//if (avmResult.revertReason) {
174176
// // resolve / enrich revert reason
@@ -285,6 +287,27 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
285287

286288
return new PublicCallRequestWithCalldata(request, calldata);
287289
}
290+
291+
// WARNING: Deduplicates by artifact name, so two different artifacts with the same name
292+
// in a single tx would only record the first one's bytecode size.
293+
async #recordBytecodeSizes(txLabel: string, calls: TestEnqueuedCall[]) {
294+
const seenArtifactNames = new Set<string>();
295+
for (const call of calls) {
296+
const artifact = await this.contractDataSource.getContractArtifact(call.address);
297+
if (!artifact || seenArtifactNames.has(artifact.name)) {
298+
continue;
299+
}
300+
seenArtifactNames.add(artifact.name);
301+
const instance = await this.contractDataSource.getContract(call.address);
302+
if (!instance) {
303+
continue;
304+
}
305+
const contractClass = await this.contractDataSource.getContractClass(instance.currentContractClassId);
306+
if (contractClass) {
307+
this.metrics.recordBytecodeSize(txLabel, artifact.name, contractClass.packedBytecode.length);
308+
}
309+
}
310+
}
288311
}
289312

290313
export function defaultGlobals() {

yarn-project/simulator/src/public/test_executor_metrics.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface PublicTxMetrics {
2121
totalDurationMs: number;
2222
manaUsed: number | undefined;
2323
totalInstructionsExecuted: number;
24+
bytecodeSizes: { contractName: string; sizeBytes: number }[];
2425
nonRevertiblePrivateInsertionsUs: number | undefined;
2526
revertiblePrivateInsertionsUs: number | undefined;
2627
enqueuedCalls: PublicEnqueuedCallMetrics[];
@@ -62,6 +63,7 @@ function createEmptyTxMetrics(): PublicTxMetrics {
6263
totalDurationMs: 0,
6364
manaUsed: 0,
6465
totalInstructionsExecuted: 0,
66+
bytecodeSizes: [],
6567
nonRevertiblePrivateInsertionsUs: undefined,
6668
revertiblePrivateInsertionsUs: undefined,
6769
enqueuedCalls: [],
@@ -172,6 +174,12 @@ export class TestExecutorMetrics implements ExecutorMetricsInterface {
172174
}
173175
}
174176

177+
recordBytecodeSize(txLabel: string, contractName: string, sizeBytes: number) {
178+
const txMetrics = this.txMetrics.get(txLabel);
179+
assert(txMetrics, `Cannot record bytecode size for unknown tx label: ${txLabel}`);
180+
txMetrics.bytecodeSizes.push({ contractName, sizeBytes });
181+
}
182+
175183
recordProverMetrics(txLabel: string, metrics: Partial<PublicTxMetrics>) {
176184
if (!this.txMetrics.has(txLabel)) {
177185
this.txMetrics.set(txLabel, createEmptyTxMetrics());
@@ -216,6 +224,15 @@ export class TestExecutorMetrics implements ExecutorMetricsInterface {
216224
) {
217225
pretty += `${INDENT0}Total instructions executed: ${fmtNum(txMetrics.totalInstructionsExecuted)}\n`;
218226
}
227+
if (
228+
(filter === PublicTxMetricsFilter.TOTALS || filter === PublicTxMetricsFilter.ALL) &&
229+
txMetrics.bytecodeSizes.length > 0
230+
) {
231+
pretty += `${INDENT0}Bytecode sizes:\n`;
232+
for (const { contractName, sizeBytes } of txMetrics.bytecodeSizes) {
233+
pretty += `${INDENT1}${contractName}: ${fmtNum(sizeBytes, 'bytes')}\n`;
234+
}
235+
}
219236
if (filter === PublicTxMetricsFilter.DURATIONS || filter === PublicTxMetricsFilter.ALL) {
220237
pretty += `${INDENT0}Private insertions:\n`;
221238
pretty += `${INDENT1}Non-revertible: ${fmtNum(txMetrics.nonRevertiblePrivateInsertionsUs! / 1_000, 'ms')}\n`;
@@ -387,6 +404,13 @@ export class TestExecutorMetrics implements ExecutorMetricsInterface {
387404
});
388405
}
389406
}
407+
for (const { contractName, sizeBytes } of txMetrics.bytecodeSizes) {
408+
data.push({
409+
name: `${txLabel}/bytecodeSizeBytes/${contractName}`,
410+
value: sizeBytes,
411+
unit: 'bytes',
412+
});
413+
}
390414
}
391415
return JSON.stringify(data, null, indent);
392416
}

0 commit comments

Comments
 (0)