Skip to content

Commit a89de5d

Browse files
authored
refactor(public/avm): from hints to the end of the world (#13459)
I had a few design goals with this PR 1. Move the merkle hinting layer to the merkle db directly (from the PublicContractsDB) 1. Make hinting a TxSimulator concept 1. Divide the tree accesses into low level and high level (like we do in C++). 1. Tighten our exports 1. Some cleanups I'm satisfied with the result, but there are some things worth noting * Now the TxSimulator wraps the merkle db into a hinting merkle db. This means that the hints will _always_ be generated and the caller has no control. However, reexecution and the TXE could benefit from a model where no hinting (or tracing) is done. I had a version of this PR where this was done via an extra PublicTreesDBFactory that was passed to the TxSimulator, but I found it too much for the moment. I think at some point we can consider having a more flexible factory type for the PublicProcessor and the TxSimulator where we can specify, e.g., merkle ops, tracing, hinting, etc and have a "Proving" factory and a "Simulation-only" factory. ## AI generated description This PR refactors the public processor and simulator components to use `MerkleTreeWriteOperations` directly instead of going through the `PublicTreesDB` abstraction. Key changes include: - Changed `PublicTxSimulator` to accept a `MerkleTreeWriteOperations` instead of `PublicTreesDB` - Moved `PublicTreesDB` to be an internal implementation detail rather than a public API - Refactored `HintingPublicTreesDB` to `HintingMerkleWriteOperations` to work directly with the merkle tree interface - Moved `SimpleContractDataSource` from `avm/fixtures` to `public/fixtures` for better organization - Added high-level methods to `PublicTreesDB` for writing note hashes and nullifiers - Simplified tree operations in `PublicPersistableStateManager` by using the new high-level methods - Updated `checkL1ToL2MessageExists` to no longer require a contract address parameter This change simplifies the codebase by reducing unnecessary abstractions and making the component dependencies more explicit.
1 parent 44c1347 commit a89de5d

27 files changed

+388
-458
lines changed

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

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,9 @@ import { TestContract } from '@aztec/noir-contracts.js/Test';
2626
import { TokenContract } from '@aztec/noir-contracts.js/Token';
2727
import type { SequencerClient } from '@aztec/sequencer-client';
2828
import type { TestSequencerClient } from '@aztec/sequencer-client/test';
29-
import {
30-
PublicContractsDB,
31-
PublicProcessorFactory,
32-
type PublicTreesDB,
33-
type PublicTxResult,
34-
TelemetryPublicTxSimulator,
35-
} from '@aztec/simulator/server';
29+
import { type PublicContractsDB, PublicProcessorFactory, TelemetryPublicTxSimulator } from '@aztec/simulator/server';
3630
import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
31+
import type { MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
3732
import { TX_ERROR_EXISTING_NULLIFIER, type Tx } from '@aztec/stdlib/tx';
3833

3934
import { jest } from '@jest/globals';
@@ -631,19 +626,19 @@ async function sendAndWait(calls: ContractFunctionInteraction[]) {
631626
const TEST_PUBLIC_TX_SIMULATION_DELAY_MS = 300;
632627

633628
class TestPublicTxSimulator extends TelemetryPublicTxSimulator {
634-
public override async simulate(tx: Tx): Promise<PublicTxResult> {
629+
public override async simulate(tx: Tx) {
635630
await sleep(TEST_PUBLIC_TX_SIMULATION_DELAY_MS);
636631
return super.simulate(tx);
637632
}
638633
}
639634
class TestPublicProcessorFactory extends PublicProcessorFactory {
640635
protected override createPublicTxSimulator(
641-
treesDB: PublicTreesDB,
636+
merkleTree: MerkleTreeWriteOperations,
642637
contractsDB: PublicContractsDB,
643638
globalVariables: GlobalVariables,
644639
doMerkleOperations: boolean,
645640
skipFeeEnforcement: boolean,
646641
) {
647-
return new TestPublicTxSimulator(treesDB, contractsDB, globalVariables, doMerkleOperations, skipFeeEnforcement);
642+
return new TestPublicTxSimulator(merkleTree, contractsDB, globalVariables, doMerkleOperations, skipFeeEnforcement);
648643
}
649644
}

yarn-project/prover-client/src/mocks/test_context.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
77
import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
88
import { computeFeePayerBalanceLeafSlot } from '@aztec/protocol-contracts/fee-juice';
99
import {
10-
PublicContractsDB,
1110
PublicProcessor,
12-
PublicTreesDB,
11+
PublicProcessorFactory,
1312
PublicTxSimulationTester,
14-
PublicTxSimulator,
1513
SimpleContractDataSource,
1614
} from '@aztec/simulator/server';
1715
import { PublicDataWrite } from '@aztec/stdlib/avm';
@@ -40,7 +38,6 @@ export class TestContext {
4038
private feePayerBalance: Fr;
4139

4240
constructor(
43-
public publicTxSimulator: PublicTxSimulator,
4441
public worldState: MerkleTreeAdminDatabase,
4542
public publicProcessor: PublicProcessor,
4643
public globalVariables: GlobalVariables,
@@ -79,26 +76,17 @@ export class TestContext {
7976

8077
// Separated dbs for public processor and prover - see public_processor for context
8178
const ws = await NativeWorldStateService.tmp(
82-
undefined /* rollupAddress */,
83-
true /* cleanupTmpDir */,
79+
/*rollupAddress=*/ undefined,
80+
/*cleanupTmpDir=*/ true,
8481
prefilledPublicData,
8582
);
8683
const merkleTrees = await ws.fork();
8784

8885
const contractDataSource = new SimpleContractDataSource();
89-
const treesDB = new PublicTreesDB(merkleTrees);
90-
const contractsDB = new PublicContractsDB(contractDataSource);
91-
9286
const tester = new PublicTxSimulationTester(merkleTrees, contractDataSource);
9387

94-
const publicTxSimulator = new PublicTxSimulator(treesDB, contractsDB, globalVariables, true);
95-
const processor = new PublicProcessor(
96-
globalVariables,
97-
treesDB,
98-
contractsDB,
99-
publicTxSimulator,
100-
new TestDateProvider(),
101-
);
88+
const processorFactory = new PublicProcessorFactory(contractDataSource, new TestDateProvider());
89+
const processor = processorFactory.create(merkleTrees, globalVariables, /*skipFeeEnforcement=*/ false);
10290

10391
let localProver: ServerCircuitProver;
10492
const config = await getEnvironmentConfig(logger);
@@ -127,7 +115,6 @@ export class TestContext {
127115
facade.start();
128116

129117
return new this(
130-
publicTxSimulator,
131118
ws,
132119
processor,
133120
globalVariables,

yarn-project/simulator/src/public/avm/avm_simulator.test.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,18 @@ import { Fq, Fr, Point } from '@aztec/foundation/fields';
1212
import type { Fieldable } from '@aztec/foundation/serialize';
1313
import { CounterContract } from '@aztec/noir-contracts.js/Counter';
1414
import { type FunctionArtifact, FunctionSelector } from '@aztec/stdlib/abi';
15-
import { PublicDataWrite } from '@aztec/stdlib/avm';
1615
import { AztecAddress } from '@aztec/stdlib/aztec-address';
1716
import { SerializableContractInstance, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
1817
import { GasFees } from '@aztec/stdlib/gas';
1918
import {
2019
computeNoteHashNonce,
21-
computePublicDataTreeLeafSlot,
2220
computeUniqueNoteHash,
2321
computeVarArgsHash,
2422
siloNoteHash,
2523
siloNullifier,
2624
} from '@aztec/stdlib/hash';
2725
import { PublicKeys } from '@aztec/stdlib/keys';
2826
import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/stdlib/testing';
29-
import { MerkleTreeId } from '@aztec/stdlib/trees';
3027
import { NativeWorldStateService } from '@aztec/world-state';
3128

3229
import { strict as assert } from 'assert';
@@ -35,6 +32,7 @@ import { mock } from 'jest-mock-extended';
3532

3633
import { SideEffectTrace } from '../../public/side_effect_trace.js';
3734
import type { PublicSideEffectTraceInterface } from '../../public/side_effect_trace_interface.js';
35+
import { SimpleContractDataSource } from '../fixtures/simple_contract_data_source.js';
3836
import { PublicContractsDB, PublicTreesDB } from '../public_db_sources.js';
3937
import type { PublicPersistableStateManager } from '../state_manager/state_manager.js';
4038
import type { AvmContext } from './avm_context.js';
@@ -57,7 +55,6 @@ import {
5755
resolveAvmTestContractAssertionMessage,
5856
resolveContractAssertionMessage,
5957
} from './fixtures/index.js';
60-
import { SimpleContractDataSource } from './fixtures/simple_contract_data_source.js';
6158
import {
6259
Add,
6360
CalldataCopy,
@@ -1112,10 +1109,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
11121109
let trace: PublicSideEffectTraceInterface;
11131110
let persistableState: PublicPersistableStateManager;
11141111

1115-
let leafSlot0: Fr;
1116-
11171112
beforeAll(async () => {
1118-
leafSlot0 = await computePublicDataTreeLeafSlot(address, slot0);
11191113
siloedNullifier0 = await siloNullifier(address, value0);
11201114
const siloedNoteHash0 = await siloNoteHash(address, value0);
11211115
const nonce = await computeNoteHashNonce(firstNullifier, noteHashIndexInTx);
@@ -1161,8 +1155,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
11611155
expect(trace.traceNewNoteHash).toHaveBeenCalledWith(uniqueNoteHash0);
11621156
});
11631157
it('Note hash check properly returns exists=false', async () => {
1164-
const treeInfo = await treesDB.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE);
1165-
const leafIndex = treeInfo.size;
1158+
const leafIndex = (await treesDB.getTreeSnapshots()).noteHashTree.nextAvailableLeafIndex;
11661159

11671160
const calldata = [uniqueNoteHash0, new Fr(leafIndex)];
11681161
const context = createContext(calldata);
@@ -1173,9 +1166,8 @@ describe('AVM simulator: transpiled Noir contracts', () => {
11731166
expect(results.output).toEqual([/*exists=*/ Fr.ZERO]);
11741167
});
11751168
it('Note hash check properly returns exists=true', async () => {
1176-
const treeInfo = await treesDB.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE);
1177-
const leafIndex = treeInfo.size;
1178-
await treesDB.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash0]);
1169+
const leafIndex = (await treesDB.getTreeSnapshots()).noteHashTree.nextAvailableLeafIndex;
1170+
await treesDB.writeNoteHash(uniqueNoteHash0);
11791171

11801172
const calldata = [uniqueNoteHash0, new Fr(leafIndex)];
11811173
const context = createContext(calldata);
@@ -1210,7 +1202,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
12101202
});
12111203
it('Nullifier check properly returns exists=true', async () => {
12121204
const calldata = [value0];
1213-
await treesDB.sequentialInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier0.toBuffer()]);
1205+
await treesDB.writeNullifier(siloedNullifier0);
12141206
const context = createContext(calldata);
12151207
const bytecode = getAvmTestContractBytecode('nullifier_exists');
12161208

@@ -1245,8 +1237,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
12451237

12461238
it('Should read value in storage (single) - written before, leaf exists', async () => {
12471239
const context = createContext();
1248-
const publicDataWrite = new PublicDataWrite(leafSlot0, value0);
1249-
await treesDB.sequentialInsert(MerkleTreeId.PUBLIC_DATA_TREE, [publicDataWrite.toBuffer()]);
1240+
await treesDB.storageWrite(context.environment.address, slot0, value0);
12501241

12511242
const bytecode = getAvmTestContractBytecode('read_storage_single');
12521243

yarn-project/simulator/src/public/avm/fixtures/avm_simulation_tester.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ import {
1616
initExecutionEnvironment,
1717
resolveContractAssertionMessage,
1818
} from '../../avm/fixtures/index.js';
19+
import { SimpleContractDataSource } from '../../fixtures/simple_contract_data_source.js';
1920
import { PublicContractsDB, PublicTreesDB } from '../../public_db_sources.js';
2021
import { PublicPersistableStateManager } from '../../state_manager/state_manager.js';
2122
import { AvmSimulator } from '../avm_simulator.js';
2223
import { BaseAvmSimulationTester } from './base_avm_simulation_tester.js';
23-
import { SimpleContractDataSource } from './simple_contract_data_source.js';
2424

2525
const TIMESTAMP = new Fr(99833);
2626
const DEFAULT_GAS_FEES = new GasFees(2, 3);

yarn-project/simulator/src/public/avm/fixtures/base_avm_simulation_tester.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { computePublicDataTreeLeafSlot, siloNullifier } from '@aztec/stdlib/hash
1111
import type { MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
1212
import { MerkleTreeId } from '@aztec/stdlib/trees';
1313

14+
import type { SimpleContractDataSource } from '../../fixtures/simple_contract_data_source.js';
1415
import { createContractClassAndInstance } from './index.js';
15-
import type { SimpleContractDataSource } from './simple_contract_data_source.js';
1616

1717
/**
1818
* An abstract test class that enables tests of real apps in the AVM without requiring e2e tests.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export * from './avm_simulator.js';
2-
export * from './fixtures/simple_contract_data_source.js';
2+
export * from '../fixtures/simple_contract_data_source.js';

yarn-project/simulator/src/public/avm/opcodes/accrued_substate.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,7 @@ export class L1ToL2MessageExists extends Instruction {
181181

182182
const msgHash = memory.get(msgHashOffset).toFr();
183183
const msgLeafIndex = memory.get(msgLeafIndexOffset).toFr();
184-
const exists = await context.persistableState.checkL1ToL2MessageExists(
185-
context.environment.address,
186-
msgHash,
187-
msgLeafIndex,
188-
);
184+
const exists = await context.persistableState.checkL1ToL2MessageExists(msgHash, msgLeafIndex);
189185
memory.set(existsOffset, exists ? new Uint1(1) : new Uint1(0));
190186
}
191187
}

yarn-project/simulator/src/common/debug_fn_name.ts renamed to yarn-project/simulator/src/public/debug_fn_name.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Fr } from '@aztec/foundation/fields';
22
import { FunctionSelector } from '@aztec/stdlib/abi';
33
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
44

5-
import type { PublicContractsDBInterface } from '../public/db_interfaces.js';
5+
import type { PublicContractsDBInterface } from './db_interfaces.js';
66

77
export async function getPublicFunctionDebugName(
88
db: PublicContractsDBInterface,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './public_tx_simulation_tester.js';
22
export * from './utils.js';
3+
export * from './simple_contract_data_source.js';

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import { NativeWorldStateService } from '@aztec/world-state';
1010

1111
import { BaseAvmSimulationTester } from '../avm/fixtures/base_avm_simulation_tester.js';
1212
import { DEFAULT_BLOCK_NUMBER, getContractFunctionAbi, getFunctionSelector } from '../avm/fixtures/index.js';
13-
import { SimpleContractDataSource } from '../avm/fixtures/simple_contract_data_source.js';
14-
import { PublicContractsDB, PublicTreesDB } from '../public_db_sources.js';
13+
import { PublicContractsDB } from '../public_db_sources.js';
1514
import { MeasuredPublicTxSimulator } from '../public_tx_simulator/measured_public_tx_simulator.js';
1615
import type { PublicTxResult } from '../public_tx_simulator/public_tx_simulator.js';
1716
import { TestExecutorMetrics } from '../test_executor_metrics.js';
17+
import { SimpleContractDataSource } from './simple_contract_data_source.js';
1818
import { createTxForPublicCalls } from './utils.js';
1919

2020
const TIMESTAMP = new Fr(99833);
@@ -47,11 +47,9 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
4747
) {
4848
super(contractDataSource, merkleTree);
4949

50-
const treesDB = new PublicTreesDB(merkleTree);
5150
const contractsDB = new PublicContractsDB(contractDataSource);
52-
5351
this.simulator = new MeasuredPublicTxSimulator(
54-
treesDB,
52+
merkleTree,
5553
contractsDB,
5654
globals,
5755
/*doMerkleOperations=*/ true,

yarn-project/simulator/src/public/avm/fixtures/simple_contract_data_source.ts renamed to yarn-project/simulator/src/public/fixtures/simple_contract_data_source.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { ContractArtifact, FunctionSelector } from '@aztec/stdlib/abi';
44
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
55
import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
66

7-
import { getFunctionSelector } from './index.js';
7+
import { getFunctionSelector } from '../avm/fixtures/index.js';
88

99
/**
1010
* This class is used during public/avm testing to function as a database of

0 commit comments

Comments
 (0)