Skip to content

Commit e19009b

Browse files
committed
feat: contract class/instance overrides for simulation
Adds `contractClasses?: ContractClassPublic[]` and `contractInstances?: ContractInstanceWithAddress[]` to `StateOverrides`, allowing simulation callers to shadow contracts in the simulator's contract DB without on-chain registration: ```ts type StateOverrides = { publicStorage?: PublicStorageOverride[]; contractClasses?: ContractClassPublic[]; contractInstances?: ContractInstanceWithAddress[]; }; ``` Plumbing: - `PublicContractsDB.addContracts({ contractClasses?, contractInstances? })`: typed-injection method that pushes overrides onto the contract DB checkpoint stack. Bypasses the log/event-extraction path. - `PublicContractsDB.addContractsFromLogs(ContractDeploymentData)`: log/event- based injection used by the C++ AVM contract provider during real execution. Disambiguated from the typed variant. - `PublicProcessorFactory.createContractsDB()` and `create(merkleTree, globalVars, config, contractsDB?)`: lets `simulatePublicCalls` populate the DB before constructing the processor, keeping production block-building paths untouched (they still call `create(...)` without the optional `contractsDB`). - `simulatePublicCalls` injects the contract overrides into a fresh `contractsDB` after `createContractsDB()` and before `factory.create(...)`. Overrides are ephemeral — they live in the per-call contract DB and are discarded after simulation; nothing reaches committed state.
1 parent 5daac75 commit e19009b

6 files changed

Lines changed: 52 additions & 18 deletions

File tree

yarn-project/aztec-node/src/aztec-node/server.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1549,7 +1549,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
15491549
maxDebugLogMemoryReads: this.config.rpcSimulatePublicMaxDebugLogMemoryReads,
15501550
}),
15511551
});
1552-
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
1552+
const contractsDB = publicProcessorFactory.createContractsDB();
1553+
if (stateOverrides) {
1554+
contractsDB.addContracts(stateOverrides);
1555+
}
1556+
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config, contractsDB);
15531557

15541558
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
15551559
const [processedTxs, failedTxs, _usedTxs, returns, debugLogs] = await processor.process([tx]);

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ export class PublicContractsDB implements PublicContractsDBInterface {
5555
this.log = createLogger('simulator:contracts-data-source', bindings);
5656
}
5757

58-
public addContracts(contractDeploymentData: ContractDeploymentData): void {
58+
/** Parses raw log data from the C++/NAPI bridge and inserts the resulting contracts into the current checkpoint. */
59+
public addContractsFromLogs(contractDeploymentData: ContractDeploymentData): void {
5960
const currentState = this.getCurrentState();
6061

6162
this.addContractClassesFromEvents(
@@ -71,8 +72,22 @@ export class PublicContractsDB implements PublicContractsDBInterface {
7172

7273
public addNewContracts(tx: Tx): void {
7374
const contractDeploymentData = AllContractDeploymentData.fromTx(tx);
74-
this.addContracts(contractDeploymentData.getNonRevertibleContractDeploymentData());
75-
this.addContracts(contractDeploymentData.getRevertibleContractDeploymentData());
75+
this.addContractsFromLogs(contractDeploymentData.getNonRevertibleContractDeploymentData());
76+
this.addContractsFromLogs(contractDeploymentData.getRevertibleContractDeploymentData());
77+
}
78+
79+
/** Inserts typed contract classes and instances directly into the current checkpoint. */
80+
public addContracts(overrides: {
81+
contractClasses?: ContractClassPublic[];
82+
contractInstances?: ContractInstanceWithAddress[];
83+
}): void {
84+
const currentState = this.getCurrentState();
85+
for (const contractClass of overrides.contractClasses ?? []) {
86+
currentState.addClass(contractClass.id, contractClass);
87+
}
88+
for (const instance of overrides.contractInstances ?? []) {
89+
currentState.addInstance(instance.address, instance);
90+
}
7691
}
7792

7893
/**

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,23 @@ export class PublicProcessorFactory {
7373
this.log = createLogger('simulator:public-processor-factory', bindings);
7474
}
7575

76+
/** Creates a fresh PublicContractsDB backed by this factory's data source. */
77+
public createContractsDB(): PublicContractsDB {
78+
return new PublicContractsDB(this.contractDataSource, this.log.getBindings());
79+
}
80+
7681
/**
7782
* Creates a new instance of a PublicProcessor.
7883
* @param globalVariables - The global variables for the block being processed.
79-
* @param skipFeeEnforcement - Allows disabling balance checks for fee estimations.
84+
* @param contractsDB - Optional pre-populated contracts DB; a fresh one is created if omitted.
8085
* @returns A new instance of a PublicProcessor.
8186
*/
8287
public create(
8388
merkleTree: MerkleTreeWriteOperations,
8489
globalVariables: GlobalVariables,
8590
config: PublicSimulatorConfig,
91+
contractsDB: PublicContractsDB = this.createContractsDB(),
8692
): PublicProcessor {
87-
const bindings = this.log.getBindings();
88-
const contractsDB = new PublicContractsDB(this.contractDataSource, bindings);
89-
9093
const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
9194
const publicTxSimulator = this.createPublicTxSimulator(guardedFork, contractsDB, globalVariables, config);
9295

@@ -97,7 +100,7 @@ export class PublicProcessorFactory {
97100
publicTxSimulator,
98101
this.dateProvider,
99102
this.telemetryClient,
100-
createLogger('simulator:public-processor', bindings),
103+
createLogger('simulator:public-processor', this.log.getBindings()),
101104
);
102105
}
103106

yarn-project/simulator/src/public/public_tx_simulator/contract_provider_for_cpp.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,8 @@ export class ContractProviderForCpp implements ContractProvider {
6161
// Construct ContractDeploymentData from plain object.
6262
const contractDeploymentData = ContractDeploymentData.fromPlainObject(rawData);
6363

64-
// Add contracts to the contracts DB
65-
this.log.trace(`Calling contractsDB.addContracts`);
66-
this.contractsDB.addContracts(contractDeploymentData);
64+
this.log.trace(`Calling contractsDB.addContractsFromLogs`);
65+
this.contractsDB.addContractsFromLogs(contractDeploymentData);
6766
};
6867

6968
public getBytecodeCommitment = async (classId: string): Promise<Buffer | undefined> => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
401401
// However, things work as expected because later calls to getters on the hintingContractsDB
402402
// will pick up the new contracts and will generate the necessary hints.
403403
// So, a consumer of the hints will always see the new contracts.
404-
this.contractsDB.addContracts(context.nonRevertibleContractDeploymentData);
404+
this.contractsDB.addContractsFromLogs(context.nonRevertibleContractDeploymentData);
405405
}
406406

407407
/**
@@ -490,7 +490,7 @@ export class PublicTxSimulator implements PublicTxSimulatorInterface {
490490
// However, things work as expected because later calls to getters on the hintingContractsDB
491491
// will pick up the new contracts and will generate the necessary hints.
492492
// So, a consumer of the hints will always see the new contracts.
493-
this.contractsDB.addContracts(context.revertibleContractDeploymentData);
493+
this.contractsDB.addContractsFromLogs(context.revertibleContractDeploymentData);
494494
}
495495

496496
private async payFee(context: PublicTxContext) {
Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
11
import { z } from 'zod';
22

3+
import { type ContractClassPublic, ContractClassPublicSchema } from '../contract/interfaces/contract_class.js';
4+
import {
5+
type ContractInstanceWithAddress,
6+
ContractInstanceWithAddressSchema,
7+
} from '../contract/interfaces/contract_instance.js';
38
import { type PublicStorageOverride, PublicStorageOverrideSchema } from './public_storage_override.js';
49

510
/**
611
* Pre-simulation state overrides. Each field is optional and additive: the simulator applies all
7-
* provided overrides to the ephemeral world-state fork before running the tx.
12+
* provided overrides to the ephemeral world-state fork (and contract DB) before running the tx.
13+
*
14+
* - `publicStorage`: write specific (contract, slot) pairs in the public-data tree
15+
* - `contractClasses`: shadow contract classes in the contract DB
16+
* - `contractInstances`: shadow contract instances in the contract DB
817
*
918
* Modeled after Foundry / Geth `eth_call` state overrides: a single argument that bundles all the
10-
* override types in one place rather than threading separate parameters through the RPC. Future
11-
* override flavors (e.g. contract class/instance shadows, nullifier overrides) can be added as
12-
* additional optional fields without changing the RPC shape.
19+
* override types in one place rather than threading separate parameters through the RPC.
1320
*/
1421
export type StateOverrides = {
1522
/** Public-storage writes to apply before simulation. */
1623
publicStorage?: PublicStorageOverride[];
24+
/** Contract classes to shadow in the contract DB for the duration of the simulation. */
25+
contractClasses?: ContractClassPublic[];
26+
/** Contract instances to shadow in the contract DB for the duration of the simulation. */
27+
contractInstances?: ContractInstanceWithAddress[];
1728
};
1829

1930
export const StateOverridesSchema = z.object({
2031
publicStorage: z.array(PublicStorageOverrideSchema).optional(),
32+
contractClasses: z.array(ContractClassPublicSchema).optional(),
33+
contractInstances: z.array(ContractInstanceWithAddressSchema).optional(),
2134
});

0 commit comments

Comments
 (0)