Skip to content

Commit 8c36ee7

Browse files
authored
feat: merge-train/avm (#19040)
See [merge-train-readme.md](https://github.com/AztecProtocol/aztec-packages/blob/next/.github/workflows/merge-train-readme.md). This is a merge-train.
2 parents a3e7562 + 2e114da commit 8c36ee7

File tree

19 files changed

+1564
-54
lines changed

19 files changed

+1564
-54
lines changed
Binary file not shown.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { createLogger } from '@aztec/foundation/log';
2+
import {
3+
TestExecutorMetrics,
4+
defaultGlobals,
5+
getSpamConfigsPerOpcode,
6+
testOpcodeSpamCase,
7+
} from '@aztec/simulator/public/fixtures';
8+
import { NativeWorldStateService } from '@aztec/world-state';
9+
10+
import { mkdirSync, writeFileSync } from 'fs';
11+
import path from 'path';
12+
13+
import { AvmProvingTester } from './avm_proving_tester.js';
14+
15+
// NOTE: this test is meant to be run locally for measurements or via bb-prover/bootstrap.sh.
16+
// Set RUN_AVM_OPCODE_SPAM=1 to enable.
17+
const describeOrSkip = process.env.RUN_AVM_OPCODE_SPAM ? describe : describe.skip;
18+
19+
describeOrSkip('AVM Opcode Spammer Proving Benchmarks', () => {
20+
const logger = createLogger('avm-opcode-spam-proving');
21+
22+
// Get test cases from the spammer config (grouped by opcode)
23+
// Limit to 2 configs per opcode otherwise this suite will take hours
24+
const groupedSpamConfigs = getSpamConfigsPerOpcode(/*maxConfigsPerOpcode=*/ 2);
25+
26+
// Shared metrics instance for benchmark collection
27+
const metrics = new TestExecutorMetrics();
28+
// Full proving only (no check-circuit mode)
29+
let worldStateService: NativeWorldStateService;
30+
let tester: AvmProvingTester;
31+
32+
afterAll(() => {
33+
if (process.env.BENCH_OUTPUT) {
34+
mkdirSync(path.dirname(process.env.BENCH_OUTPUT), { recursive: true });
35+
writeFileSync(process.env.BENCH_OUTPUT, metrics.toGithubActionBenchmarkJSON());
36+
} else if (process.env.BENCH_OUTPUT_MD) {
37+
writeFileSync(process.env.BENCH_OUTPUT_MD, metrics.toPrettyString());
38+
} else {
39+
logger.info(`\n`);
40+
logger.info(metrics.toPrettyString());
41+
}
42+
});
43+
44+
beforeEach(async () => {
45+
worldStateService = await NativeWorldStateService.tmp();
46+
// FULL PROVING! Not check-circuit.
47+
tester = await AvmProvingTester.new(
48+
worldStateService,
49+
/*checkCircuitOnly=*/ false,
50+
/*globals=*/ defaultGlobals(),
51+
metrics,
52+
);
53+
tester.setMetricsPrefix(`FullProving Opcode Spam`);
54+
});
55+
56+
afterEach(async () => {
57+
await worldStateService.close();
58+
});
59+
60+
describe.each(groupedSpamConfigs)('$opcode', ({ configs }) => {
61+
it.each(configs)(
62+
'$label',
63+
async config => {
64+
await testOpcodeSpamCase(tester, config);
65+
},
66+
600_000,
67+
);
68+
});
69+
});

yarn-project/ivc-integration/src/avm_integration.test.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { createLogger } from '@aztec/foundation/log';
55
import { mapAvmCircuitPublicInputsToNoir } from '@aztec/noir-protocol-circuits-types/server';
66
import { AvmTestContractArtifact } from '@aztec/noir-test-contracts.js/AvmTest';
77
import { PublicTxSimulationTester, bulkTest, executeAvmMinimalPublicTx } from '@aztec/simulator/public/fixtures';
8-
import { AvmCircuitInputs } from '@aztec/stdlib/avm';
8+
import { AvmCircuitInputs, PublicSimulatorConfig } from '@aztec/stdlib/avm';
99
import { RecursiveProof } from '@aztec/stdlib/proofs';
1010
import { VerificationKeyAsFields } from '@aztec/stdlib/vks';
1111
import { NativeWorldStateService } from '@aztec/world-state/native';
@@ -34,6 +34,15 @@ jest.setTimeout(120_000);
3434

3535
const logger = createLogger('ivc-integration:test:avm-integration');
3636

37+
const simConfig: PublicSimulatorConfig = PublicSimulatorConfig.from({
38+
skipFeeEnforcement: false,
39+
collectCallMetadata: true, // For results.
40+
collectDebugLogs: false,
41+
collectHints: true, // Required for proving!
42+
collectPublicInputs: true, // Required for proving!
43+
collectStatistics: false,
44+
});
45+
3746
async function proveMockPublicBaseRollup(
3847
avmCircuitInputs: AvmCircuitInputs,
3948
bbWorkingDirectory: string,
@@ -107,7 +116,13 @@ describe('AVM Integration', () => {
107116
bbWorkingDirectory = await getWorkingDirectory('bb-avm-integration-');
108117

109118
worldStateService = await NativeWorldStateService.tmp();
110-
simTester = await PublicTxSimulationTester.create(worldStateService);
119+
simTester = await PublicTxSimulationTester.create(
120+
worldStateService,
121+
/*globals=*/ undefined, // default
122+
/*metrics=*/ undefined,
123+
/*useCppSimulator=*/ true,
124+
simConfig,
125+
);
111126
});
112127

113128
afterEach(async () => {

yarn-project/ivc-integration/src/rollup_ivc_integration.test.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { createLogger } from '@aztec/foundation/log';
55
import { mapAvmCircuitPublicInputsToNoir } from '@aztec/noir-protocol-circuits-types/server';
66
import { AvmTestContractArtifact } from '@aztec/noir-test-contracts.js/AvmTest';
77
import { PublicTxSimulationTester, bulkTest } from '@aztec/simulator/public/fixtures';
8-
import { AvmCircuitInputs, AvmCircuitPublicInputs } from '@aztec/stdlib/avm';
8+
import { AvmCircuitInputs, AvmCircuitPublicInputs, PublicSimulatorConfig } from '@aztec/stdlib/avm';
99
import { RecursiveProof } from '@aztec/stdlib/proofs';
1010
import { VerificationKeyAsFields } from '@aztec/stdlib/vks';
1111
import { NativeWorldStateService } from '@aztec/world-state/native';
@@ -38,6 +38,15 @@ jest.setTimeout(150_000);
3838

3939
const logger = createLogger('ivc-integration:test:rollup-native');
4040

41+
const simConfig: PublicSimulatorConfig = PublicSimulatorConfig.from({
42+
skipFeeEnforcement: false,
43+
collectCallMetadata: true, // For results.
44+
collectDebugLogs: false,
45+
collectHints: true, // Required for proving!
46+
collectPublicInputs: true, // Required for proving!
47+
collectStatistics: false,
48+
});
49+
4150
describe('Rollup IVC Integration', () => {
4251
let bbBinaryPath: string;
4352

@@ -72,7 +81,13 @@ describe('Rollup IVC Integration', () => {
7281
const avmWorkingDirectory = await getWorkingDirectory('bb-rollup-ivc-integration-avm-');
7382

7483
const worldStateService = await NativeWorldStateService.tmp();
75-
const simTester = await PublicTxSimulationTester.create(worldStateService);
84+
const simTester = await PublicTxSimulationTester.create(
85+
worldStateService,
86+
/*globals=*/ undefined, // default
87+
/*metrics=*/ undefined,
88+
/*useCppSimulator=*/ true,
89+
simConfig,
90+
);
7691
const avmSimulationResult = await bulkTest(simTester, logger, AvmTestContractArtifact);
7792
await worldStateService.close();
7893
expect(avmSimulationResult.revertCode.isOK()).toBe(true);

yarn-project/simulator/artifacts/avm_minimal_inputs.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"gasFees": { "feePerDaGas": "2", "feePerL2Gas": "3" }
1212
},
1313
"tx": {
14-
"hash": "0x02440a894870f8078ce6065f76c2f598e2f3df9719f1a78038057c20eb096516",
14+
"hash": "0x0fe93625c73eb2fd8131834fac90f24341cc1b87bdfa9c09f7f61c7f5f943bc5",
1515
"gasSettings": {
1616
"gasLimits": { "daGas": 12582912, "l2Gas": 6000000 },
1717
"teardownGasLimits": { "daGas": 0, "l2Gas": 0 },
@@ -34,9 +34,9 @@
3434
"msgSender": "0x000000000000000000000000000000000000000000000000000000000000002a",
3535
"contractAddress": "0x03deefb26b3b88ef515e0ce5bd1402ea3a7c12569597cddb19ce6260444d625f",
3636
"isStaticCall": false,
37-
"calldataHash": "0x210b9efa10b8e4522089aa9b227e18fb47dbf4c9a53b37225416d7900600be65"
37+
"calldataHash": "0x0d11f0bac132909f8a77a45be32a0dfad89702d957f3f1963b790545be3f966c"
3838
},
39-
"calldata": ["0x000000000000000000000000000000000000000000000000000000000d6ab3ae"]
39+
"calldata": []
4040
}
4141
],
4242
"teardownEnqueuedCall": null,
@@ -856,7 +856,7 @@
856856
"msgSender": "0x000000000000000000000000000000000000000000000000000000000000002a",
857857
"contractAddress": "0x03deefb26b3b88ef515e0ce5bd1402ea3a7c12569597cddb19ce6260444d625f",
858858
"isStaticCall": false,
859-
"calldataHash": "0x210b9efa10b8e4522089aa9b227e18fb47dbf4c9a53b37225416d7900600be65"
859+
"calldataHash": "0x0d11f0bac132909f8a77a45be32a0dfad89702d957f3f1963b790545be3f966c"
860860
},
861861
{
862862
"msgSender": "0x0000000000000000000000000000000000000000000000000000000000000000",

yarn-project/simulator/src/public/avm/avm_memory_types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ const TAG_FOR_MEM_VAL = new Map<string, TypeTag>([
416416
['Uint128', TypeTag.UINT128],
417417
]);
418418

419-
const VALID_TAGS = new Set([
419+
export const VALID_TAGS = new Set([
420420
TypeTag.FIELD,
421421
TypeTag.UINT1,
422422
TypeTag.UINT8,
@@ -426,7 +426,7 @@ const VALID_TAGS = new Set([
426426
TypeTag.UINT128,
427427
]);
428428

429-
const INTEGRAL_TAGS = new Set([
429+
export const INTEGRAL_TAGS = new Set([
430430
TypeTag.UINT1,
431431
TypeTag.UINT8,
432432
TypeTag.UINT16,

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ export async function getPublicFunctionDebugName(
1313
if (!calldata[0]) {
1414
return `<calldata[0] undefined> (Contract Address: ${contractAddress})`;
1515
}
16-
const selector = FunctionSelector.fromField(calldata[0]);
17-
return (await db.getDebugFunctionName(contractAddress, selector)) ?? selector.toString();
16+
const fallbackName = `<calldata[0]:${calldata[0].toString()}> (Contract Address: ${contractAddress})`;
17+
const selector = FunctionSelector.fromFieldOrUndefined(calldata[0]);
18+
if (!selector) {
19+
return fallbackName;
20+
}
21+
return (await db.getDebugFunctionName(contractAddress, selector)) ?? fallbackName;
1822
}
1923

2024
/**
@@ -34,7 +38,10 @@ export async function getPublicFunctionSelectorAndName(
3438
if (!calldata[0]) {
3539
return {};
3640
}
37-
const selector = FunctionSelector.fromField(calldata[0]);
41+
const selector = FunctionSelector.fromFieldOrUndefined(calldata[0]);
42+
if (!selector) {
43+
return {};
44+
}
3845
const debugName = await db.getDebugFunctionName(contractAddress, selector);
3946
return {
4047
functionSelector: selector,
Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,83 @@
11
import { FunctionType, emptyContractArtifact, emptyFunctionArtifact } from '@aztec/stdlib/abi';
22
import type { PublicTxResult } from '@aztec/stdlib/avm';
33
import { AztecAddress } from '@aztec/stdlib/aztec-address';
4+
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
45

56
import { PublicTxSimulationTester } from './public_tx_simulation_tester.js';
67

78
/**
8-
*
9-
* Test custom bytecode (simulation or proving) with the provided bytecode.
9+
* Deploy a contract with the provided bytecode.
1010
* @param bytecode - The bytecode buffer to use
11-
* @param tester - The tester to use (simulation or proving)
12-
* @param txLabel - The label of the transaction
13-
* @param contractName - The name of the contract (default: 'CustomBytecodeContract')
11+
* @param tester - The tester to use
12+
* @param contractName - The name of the contract
13+
* @param deployer - The deployer address
14+
* @returns The deployed contract instance
1415
*/
15-
export async function testCustomBytecode(
16+
export async function deployCustomBytecode(
1617
bytecode: Buffer,
1718
tester: PublicTxSimulationTester,
18-
txLabel: string,
1919
contractName: string = 'CustomBytecodeContract',
20-
): Promise<PublicTxResult> {
21-
const deployer = AztecAddress.fromNumber(42);
22-
20+
deployer: AztecAddress = AztecAddress.fromNumber(42),
21+
): Promise<ContractInstanceWithAddress> {
2322
const contractArtifact = emptyContractArtifact();
2423
contractArtifact.name = contractName;
2524
contractArtifact.functions = [emptyFunctionArtifact()];
25+
// We use name 'public_dispatch' since that is what is expected
26+
// in a ContractArtifact. But function selectors are not required
27+
// when executing since the custom bytecode likely has no dispatch.
2628
contractArtifact.functions[0].name = 'public_dispatch';
2729
contractArtifact.functions[0].functionType = FunctionType.PUBLIC;
2830
contractArtifact.functions[0].bytecode = bytecode;
2931

30-
const testContract = await tester.registerAndDeployContract(
32+
// return the contract instance
33+
return await tester.registerAndDeployContract(
3134
/*constructorArgs=*/ [],
3235
deployer,
3336
/*contractArtifact=*/ contractArtifact,
3437
);
38+
}
3539

40+
/**
41+
* Execute a custom bytecode contract.
42+
* @param contract - The contract instance to execute
43+
* @param tester - The tester to use
44+
* @param txLabel - The label of the transaction
45+
* @param calldata - The calldata to use
46+
* @returns The execution result
47+
*/
48+
export async function executeCustomBytecode(
49+
contract: ContractInstanceWithAddress,
50+
tester: PublicTxSimulationTester,
51+
txLabel: string = 'CustomBytecodeTest',
52+
calldata: any[] = [],
53+
): Promise<PublicTxResult> {
3654
// EXECUTE! This means that if using AvmProvingTester subclass, it will PROVE the transaction!
3755
return await tester.executeTxWithLabel(
3856
/*txLabel=*/ txLabel,
39-
/*sender=*/ deployer,
57+
/*sender=*/ contract.deployer,
4058
/*setupCalls=*/ [],
41-
/*appCalls=*/ [
42-
{
43-
address: testContract.address,
44-
fnName: 'public_dispatch',
45-
args: [],
46-
},
47-
],
59+
/*appCalls=*/ [{ address: contract.address, args: calldata }],
4860
);
4961
}
62+
63+
/**
64+
* Deploy and execute a custom bytecode contract.
65+
* @param bytecode - The bytecode buffer to use
66+
* @param tester - The tester to use
67+
* @param txLabel - The label of the transaction
68+
* @param contractName - The name of the contract
69+
* @param deployer - The deployer address
70+
* @param calldata - The calldata to use
71+
* @returns The execution result
72+
*/
73+
export async function deployAndExecuteCustomBytecode(
74+
bytecode: Buffer,
75+
tester: PublicTxSimulationTester,
76+
txLabel: string = 'CustomBytecodeTest',
77+
contractName: string = 'CustomBytecodeContract',
78+
deployer: AztecAddress = AztecAddress.fromNumber(42),
79+
calldata: any[] = [],
80+
): Promise<PublicTxResult> {
81+
const testContract = await deployCustomBytecode(bytecode, tester, contractName, deployer);
82+
return await executeCustomBytecode(testContract, tester, txLabel, calldata);
83+
}

0 commit comments

Comments
 (0)