@aztec/simulator 0.87.6 → 1.0.0-nightly.20250604
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/public/avm/avm_gas.d.ts +4 -5
- package/dest/public/avm/avm_gas.d.ts.map +1 -1
- package/dest/public/avm/avm_gas.js +29 -19
- package/dest/public/avm/fixtures/utils.js +1 -1
- package/dest/public/avm/opcodes/accrued_substate.d.ts.map +1 -1
- package/dest/public/avm/opcodes/accrued_substate.js +8 -7
- package/dest/public/avm/opcodes/addressing_mode.d.ts +2 -0
- package/dest/public/avm/opcodes/addressing_mode.d.ts.map +1 -1
- package/dest/public/avm/opcodes/addressing_mode.js +6 -0
- package/dest/public/avm/opcodes/arithmetic.d.ts.map +1 -1
- package/dest/public/avm/opcodes/arithmetic.js +1 -1
- package/dest/public/avm/opcodes/bitwise.d.ts +5 -1
- package/dest/public/avm/opcodes/bitwise.d.ts.map +1 -1
- package/dest/public/avm/opcodes/bitwise.js +17 -2
- package/dest/public/avm/opcodes/comparators.d.ts.map +1 -1
- package/dest/public/avm/opcodes/comparators.js +1 -1
- package/dest/public/avm/opcodes/contract.d.ts.map +1 -1
- package/dest/public/avm/opcodes/contract.js +1 -1
- package/dest/public/avm/opcodes/control_flow.d.ts.map +1 -1
- package/dest/public/avm/opcodes/control_flow.js +4 -4
- package/dest/public/avm/opcodes/conversion.d.ts +1 -0
- package/dest/public/avm/opcodes/conversion.d.ts.map +1 -1
- package/dest/public/avm/opcodes/conversion.js +263 -2
- package/dest/public/avm/opcodes/ec_add.d.ts.map +1 -1
- package/dest/public/avm/opcodes/ec_add.js +1 -1
- package/dest/public/avm/opcodes/environment_getters.d.ts.map +1 -1
- package/dest/public/avm/opcodes/environment_getters.js +1 -1
- package/dest/public/avm/opcodes/external_calls.d.ts.map +1 -1
- package/dest/public/avm/opcodes/external_calls.js +6 -8
- package/dest/public/avm/opcodes/hashing.d.ts.map +1 -1
- package/dest/public/avm/opcodes/hashing.js +3 -3
- package/dest/public/avm/opcodes/instruction.d.ts +9 -3
- package/dest/public/avm/opcodes/instruction.d.ts.map +1 -1
- package/dest/public/avm/opcodes/instruction.js +12 -7
- package/dest/public/avm/opcodes/memory.d.ts.map +1 -1
- package/dest/public/avm/opcodes/memory.js +8 -6
- package/dest/public/avm/opcodes/misc.js +1 -3
- package/dest/public/avm/opcodes/storage.d.ts.map +1 -1
- package/dest/public/avm/opcodes/storage.js +5 -3
- package/dest/public/fixtures/index.d.ts +1 -0
- package/dest/public/fixtures/index.d.ts.map +1 -1
- package/dest/public/fixtures/index.js +1 -0
- package/dest/public/fixtures/minimal_public_tx.d.ts +9 -0
- package/dest/public/fixtures/minimal_public_tx.d.ts.map +1 -0
- package/dest/public/fixtures/minimal_public_tx.js +43 -0
- package/dest/public/fixtures/utils.js +3 -3
- package/dest/public/hinting_db_sources.d.ts +3 -0
- package/dest/public/hinting_db_sources.d.ts.map +1 -1
- package/dest/public/hinting_db_sources.js +9 -0
- package/dest/public/index.d.ts +2 -1
- package/dest/public/index.d.ts.map +1 -1
- package/dest/public/index.js +2 -1
- package/dest/public/public_db_sources.d.ts.map +1 -1
- package/dest/public/public_db_sources.js +2 -2
- package/dest/public/public_processor/guarded_merkle_tree.d.ts +44 -0
- package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -0
- package/dest/public/public_processor/guarded_merkle_tree.js +105 -0
- package/dest/public/public_processor/public_processor.d.ts +6 -16
- package/dest/public/public_processor/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor/public_processor.js +32 -16
- package/dest/public/public_tx_simulator/public_tx_context.d.ts +3 -1
- package/dest/public/public_tx_simulator/public_tx_context.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/public_tx_context.js +17 -14
- package/dest/public/public_tx_simulator/public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator/public_tx_simulator.js +0 -3
- package/dest/public/side_effect_trace.d.ts +4 -1
- package/dest/public/side_effect_trace.d.ts.map +1 -1
- package/dest/public/side_effect_trace.js +13 -2
- package/dest/public/side_effect_trace_interface.d.ts +1 -0
- package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
- package/dest/public/state_manager/state_manager.d.ts +1 -0
- package/dest/public/state_manager/state_manager.d.ts.map +1 -1
- package/dest/public/state_manager/state_manager.js +3 -0
- package/package.json +15 -15
- package/src/public/avm/avm_gas.ts +21 -15
- package/src/public/avm/fixtures/utils.ts +1 -1
- package/src/public/avm/opcodes/accrued_substate.ts +23 -7
- package/src/public/avm/opcodes/addressing_mode.ts +8 -0
- package/src/public/avm/opcodes/arithmetic.ts +3 -1
- package/src/public/avm/opcodes/bitwise.ts +26 -2
- package/src/public/avm/opcodes/comparators.ts +3 -1
- package/src/public/avm/opcodes/contract.ts +3 -1
- package/src/public/avm/opcodes/control_flow.ts +6 -4
- package/src/public/avm/opcodes/conversion.ts +21 -2
- package/src/public/avm/opcodes/ec_add.ts +3 -1
- package/src/public/avm/opcodes/environment_getters.ts +3 -1
- package/src/public/avm/opcodes/external_calls.ts +16 -8
- package/src/public/avm/opcodes/hashing.ts +11 -3
- package/src/public/avm/opcodes/instruction.ts +14 -7
- package/src/public/avm/opcodes/memory.ts +23 -6
- package/src/public/avm/opcodes/misc.ts +4 -4
- package/src/public/avm/opcodes/storage.ts +13 -3
- package/src/public/fixtures/index.ts +1 -0
- package/src/public/fixtures/minimal_public_tx.ts +57 -0
- package/src/public/fixtures/utils.ts +3 -3
- package/src/public/hinting_db_sources.ts +15 -0
- package/src/public/index.ts +2 -1
- package/src/public/public_db_sources.ts +3 -13
- package/src/public/public_processor/guarded_merkle_tree.ts +148 -0
- package/src/public/public_processor/public_processor.ts +47 -34
- package/src/public/public_tx_simulator/public_tx_context.ts +37 -19
- package/src/public/public_tx_simulator/public_tx_simulator.ts +0 -3
- package/src/public/side_effect_trace.ts +13 -0
- package/src/public/side_effect_trace_interface.ts +1 -0
- package/src/public/state_manager/state_manager.ts +4 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { SerialQueue } from '@aztec/foundation/queue';
|
|
2
|
+
import type { IndexedTreeLeafPreimage, SiblingPath } from '@aztec/foundation/trees';
|
|
3
|
+
import type {
|
|
4
|
+
BatchInsertionResult,
|
|
5
|
+
IndexedTreeId,
|
|
6
|
+
MerkleTreeId,
|
|
7
|
+
MerkleTreeLeafType,
|
|
8
|
+
MerkleTreeWriteOperations,
|
|
9
|
+
SequentialInsertionResult,
|
|
10
|
+
TreeInfo,
|
|
11
|
+
} from '@aztec/stdlib/trees';
|
|
12
|
+
import type { BlockHeader, StateReference } from '@aztec/stdlib/tx';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Wraps an instance of `MerkleTreeWriteOperations` to allow the sequencer to gate access.
|
|
16
|
+
* If transactions execution goes past the deadline, the simulator will continue to execute and update the world state
|
|
17
|
+
* The public processor however requires that the world state remain constant after the deadline in order to finalise the block
|
|
18
|
+
* The public processor provides this implementation of MerkleTreeWriteOperations to the simulator
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export class GuardedMerkleTreeOperations implements MerkleTreeWriteOperations {
|
|
22
|
+
private isStopped = false;
|
|
23
|
+
private serialQueue = new SerialQueue();
|
|
24
|
+
|
|
25
|
+
constructor(private target: MerkleTreeWriteOperations) {
|
|
26
|
+
this.serialQueue.start();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private guard() {
|
|
30
|
+
if (this.isStopped) {
|
|
31
|
+
throw new Error('Merkle tree access has been stopped');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Executes the provided function only if the guard is not stopped.
|
|
36
|
+
private guardAndPush<T>(fn: () => Promise<T>): Promise<T> {
|
|
37
|
+
this.guard();
|
|
38
|
+
return this.serialQueue.put(() => {
|
|
39
|
+
this.guard();
|
|
40
|
+
return fn();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public getUnderlyingFork(): MerkleTreeWriteOperations {
|
|
45
|
+
return this.target;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Stops all further access to the merkle trees via this object
|
|
49
|
+
async stop(): Promise<void> {
|
|
50
|
+
await this.serialQueue.put(() => {
|
|
51
|
+
this.isStopped = true;
|
|
52
|
+
return Promise.resolve();
|
|
53
|
+
});
|
|
54
|
+
return this.serialQueue.end();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Proxy all methods to the target
|
|
58
|
+
appendLeaves<ID extends MerkleTreeId>(treeId: ID, leaves: MerkleTreeLeafType<ID>[]): Promise<void> {
|
|
59
|
+
return this.guardAndPush(() => this.target.appendLeaves(treeId, leaves));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
updateArchive(header: BlockHeader): Promise<void> {
|
|
63
|
+
return this.guardAndPush(() => this.target.updateArchive(header));
|
|
64
|
+
}
|
|
65
|
+
batchInsert<TreeHeight extends number, SubtreeSiblingPathHeight extends number, ID extends IndexedTreeId>(
|
|
66
|
+
treeId: ID,
|
|
67
|
+
leaves: Buffer[],
|
|
68
|
+
subtreeHeight: number,
|
|
69
|
+
): Promise<BatchInsertionResult<TreeHeight, SubtreeSiblingPathHeight>> {
|
|
70
|
+
return this.guardAndPush(() => this.target.batchInsert(treeId, leaves, subtreeHeight));
|
|
71
|
+
}
|
|
72
|
+
sequentialInsert<TreeHeight extends number, ID extends IndexedTreeId>(
|
|
73
|
+
treeId: ID,
|
|
74
|
+
leaves: Buffer[],
|
|
75
|
+
): Promise<SequentialInsertionResult<TreeHeight>> {
|
|
76
|
+
return this.guardAndPush(() => this.target.sequentialInsert(treeId, leaves));
|
|
77
|
+
}
|
|
78
|
+
close(): Promise<void> {
|
|
79
|
+
return this.guardAndPush(() => this.target.close());
|
|
80
|
+
}
|
|
81
|
+
getTreeInfo(treeId: MerkleTreeId): Promise<TreeInfo> {
|
|
82
|
+
return this.guardAndPush(() => this.target.getTreeInfo(treeId));
|
|
83
|
+
}
|
|
84
|
+
getStateReference(): Promise<StateReference> {
|
|
85
|
+
return this.guardAndPush(() => this.target.getStateReference());
|
|
86
|
+
}
|
|
87
|
+
getInitialHeader(): BlockHeader {
|
|
88
|
+
return this.target.getInitialHeader();
|
|
89
|
+
}
|
|
90
|
+
getSiblingPath<N extends number>(treeId: MerkleTreeId, index: bigint): Promise<SiblingPath<N>> {
|
|
91
|
+
return this.guardAndPush(() => this.target.getSiblingPath(treeId, index));
|
|
92
|
+
}
|
|
93
|
+
getPreviousValueIndex<ID extends IndexedTreeId>(
|
|
94
|
+
treeId: ID,
|
|
95
|
+
value: bigint,
|
|
96
|
+
): Promise<{ index: bigint; alreadyPresent: boolean } | undefined> {
|
|
97
|
+
return this.guardAndPush(() => this.target.getPreviousValueIndex(treeId, value));
|
|
98
|
+
}
|
|
99
|
+
getLeafPreimage<ID extends IndexedTreeId>(treeId: ID, index: bigint): Promise<IndexedTreeLeafPreimage | undefined> {
|
|
100
|
+
return this.guardAndPush(() => this.target.getLeafPreimage(treeId, index));
|
|
101
|
+
}
|
|
102
|
+
findLeafIndices<ID extends MerkleTreeId>(
|
|
103
|
+
treeId: ID,
|
|
104
|
+
values: MerkleTreeLeafType<ID>[],
|
|
105
|
+
): Promise<(bigint | undefined)[]> {
|
|
106
|
+
return this.guardAndPush(() => this.target.findLeafIndices(treeId, values));
|
|
107
|
+
}
|
|
108
|
+
findLeafIndicesAfter<ID extends MerkleTreeId>(
|
|
109
|
+
treeId: ID,
|
|
110
|
+
values: MerkleTreeLeafType<ID>[],
|
|
111
|
+
startIndex: bigint,
|
|
112
|
+
): Promise<(bigint | undefined)[]> {
|
|
113
|
+
return this.guardAndPush(() => this.target.findLeafIndicesAfter(treeId, values, startIndex));
|
|
114
|
+
}
|
|
115
|
+
getLeafValue<ID extends MerkleTreeId>(
|
|
116
|
+
treeId: ID,
|
|
117
|
+
index: bigint,
|
|
118
|
+
): Promise<MerkleTreeLeafType<typeof treeId> | undefined> {
|
|
119
|
+
return this.guardAndPush(() => this.target.getLeafValue(treeId, index));
|
|
120
|
+
}
|
|
121
|
+
getBlockNumbersForLeafIndices<ID extends MerkleTreeId>(
|
|
122
|
+
treeId: ID,
|
|
123
|
+
leafIndices: bigint[],
|
|
124
|
+
): Promise<(bigint | undefined)[]> {
|
|
125
|
+
return this.guardAndPush(() => this.target.getBlockNumbersForLeafIndices(treeId, leafIndices));
|
|
126
|
+
}
|
|
127
|
+
createCheckpoint(): Promise<void> {
|
|
128
|
+
return this.guardAndPush(() => this.target.createCheckpoint());
|
|
129
|
+
}
|
|
130
|
+
commitCheckpoint(): Promise<void> {
|
|
131
|
+
return this.guardAndPush(() => this.target.commitCheckpoint());
|
|
132
|
+
}
|
|
133
|
+
revertCheckpoint(): Promise<void> {
|
|
134
|
+
return this.guardAndPush(() => this.target.revertCheckpoint());
|
|
135
|
+
}
|
|
136
|
+
commitAllCheckpoints(): Promise<void> {
|
|
137
|
+
return this.guardAndPush(() => this.target.commitAllCheckpoints());
|
|
138
|
+
}
|
|
139
|
+
revertAllCheckpoints(): Promise<void> {
|
|
140
|
+
return this.guardAndPush(() => this.target.revertAllCheckpoints());
|
|
141
|
+
}
|
|
142
|
+
findSiblingPaths<ID extends MerkleTreeId, N extends number>(
|
|
143
|
+
treeId: ID,
|
|
144
|
+
values: MerkleTreeLeafType<ID>[],
|
|
145
|
+
): Promise<(SiblingPath<N> | undefined)[]> {
|
|
146
|
+
return this.guardAndPush(() => this.target.findSiblingPaths(treeId, values));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -10,7 +10,11 @@ import { PublicDataWrite } from '@aztec/stdlib/avm';
|
|
|
10
10
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
11
11
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
12
12
|
import { Gas } from '@aztec/stdlib/gas';
|
|
13
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
MerkleTreeWriteOperations,
|
|
15
|
+
PublicProcessorLimits,
|
|
16
|
+
PublicProcessorValidator,
|
|
17
|
+
} from '@aztec/stdlib/interfaces/server';
|
|
14
18
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
15
19
|
import {
|
|
16
20
|
type FailedTx,
|
|
@@ -35,6 +39,7 @@ import { ForkCheckpoint } from '@aztec/world-state/native';
|
|
|
35
39
|
|
|
36
40
|
import { PublicContractsDB, PublicTreesDB } from '../public_db_sources.js';
|
|
37
41
|
import { type PublicTxSimulator, TelemetryPublicTxSimulator } from '../public_tx_simulator/index.js';
|
|
42
|
+
import { GuardedMerkleTreeOperations } from './guarded_merkle_tree.js';
|
|
38
43
|
import { PublicProcessorMetrics } from './public_processor_metrics.js';
|
|
39
44
|
|
|
40
45
|
/**
|
|
@@ -61,8 +66,10 @@ export class PublicProcessorFactory {
|
|
|
61
66
|
clientInitiatedSimulation: boolean = false,
|
|
62
67
|
): PublicProcessor {
|
|
63
68
|
const contractsDB = new PublicContractsDB(this.contractDataSource);
|
|
69
|
+
|
|
70
|
+
const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
|
|
64
71
|
const publicTxSimulator = this.createPublicTxSimulator(
|
|
65
|
-
|
|
72
|
+
guardedFork,
|
|
66
73
|
contractsDB,
|
|
67
74
|
globalVariables,
|
|
68
75
|
/*doMerkleOperations=*/ true,
|
|
@@ -72,7 +79,7 @@ export class PublicProcessorFactory {
|
|
|
72
79
|
|
|
73
80
|
return new PublicProcessor(
|
|
74
81
|
globalVariables,
|
|
75
|
-
|
|
82
|
+
guardedFork,
|
|
76
83
|
contractsDB,
|
|
77
84
|
publicTxSimulator,
|
|
78
85
|
this.dateProvider,
|
|
@@ -116,7 +123,7 @@ export class PublicProcessor implements Traceable {
|
|
|
116
123
|
|
|
117
124
|
constructor(
|
|
118
125
|
protected globalVariables: GlobalVariables,
|
|
119
|
-
private
|
|
126
|
+
private guardedMerkleTree: GuardedMerkleTreeOperations,
|
|
120
127
|
protected contractsDB: PublicContractsDB,
|
|
121
128
|
protected publicTxSimulator: PublicTxSimulator,
|
|
122
129
|
private dateProvider: DateProvider,
|
|
@@ -139,16 +146,8 @@ export class PublicProcessor implements Traceable {
|
|
|
139
146
|
*/
|
|
140
147
|
public async process(
|
|
141
148
|
txs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
142
|
-
limits: {
|
|
143
|
-
|
|
144
|
-
maxBlockSize?: number;
|
|
145
|
-
maxBlockGas?: Gas;
|
|
146
|
-
deadline?: Date;
|
|
147
|
-
} = {},
|
|
148
|
-
validator: {
|
|
149
|
-
preprocessValidator?: TxValidator<Tx>;
|
|
150
|
-
nullifierCache?: { addNullifiers: (nullifiers: Buffer[]) => void };
|
|
151
|
-
} = {},
|
|
149
|
+
limits: PublicProcessorLimits = {},
|
|
150
|
+
validator: PublicProcessorValidator = {},
|
|
152
151
|
): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[]]> {
|
|
153
152
|
const { maxTransactions, maxBlockSize, deadline, maxBlockGas } = limits;
|
|
154
153
|
const { preprocessValidator, nullifierCache } = validator;
|
|
@@ -226,7 +225,9 @@ export class PublicProcessor implements Traceable {
|
|
|
226
225
|
// We checkpoint the transaction here, then within the try/catch we
|
|
227
226
|
// 1. Revert the checkpoint if the tx fails or needs to be discarded for any reason
|
|
228
227
|
// 2. Commit the transaction in the finally block. Note that by using the ForkCheckpoint lifecycle only the first commit/revert takes effect
|
|
229
|
-
|
|
228
|
+
// By doing this, every transaction starts on a fresh checkpoint and it's state updates only make it to the fork if this checkpoint is committed.
|
|
229
|
+
// Note: We use the underlying fork here not the guarded one, this ensures that it's not impacted by stopping the guarded version
|
|
230
|
+
const checkpoint = await ForkCheckpoint.new(this.guardedMerkleTree.getUnderlyingFork());
|
|
230
231
|
|
|
231
232
|
try {
|
|
232
233
|
const [processedTx, returnValues] = await this.processTx(tx, deadline);
|
|
@@ -256,19 +257,37 @@ export class PublicProcessor implements Traceable {
|
|
|
256
257
|
totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
|
|
257
258
|
totalSizeInBytes += txSize;
|
|
258
259
|
} catch (err: any) {
|
|
259
|
-
// Roll back state to start of TX before proceeding to next TX
|
|
260
|
-
await checkpoint.revert();
|
|
261
260
|
if (err?.name === 'PublicProcessorTimeoutError') {
|
|
262
261
|
this.log.warn(`Stopping tx processing due to timeout.`);
|
|
262
|
+
// We hit the transaction execution deadline.
|
|
263
|
+
// There may still be a transaction executing. We stop the guarded fork to prevent any further access to the world state.
|
|
264
|
+
await this.guardedMerkleTree.stop();
|
|
265
|
+
|
|
266
|
+
// We now know there can't be any further access to world state. The fork is in a state where there is:
|
|
267
|
+
// 1. At least one outstanding checkpoint that has not been committed (the one created before we processed the tx).
|
|
268
|
+
// 2. Possible state updates on that checkpoint or any others created during execution.
|
|
269
|
+
|
|
270
|
+
// First we revert a checkpoint as managed by the ForkCheckpoint. This will revert whatever is the current checkpoint
|
|
271
|
+
// which may not be the one originally created by this object. But that is ok, we do this to fulfil the ForkCheckpoint
|
|
272
|
+
// lifecycle expectations and ensure it doesn't attempt to commit later on.
|
|
273
|
+
await checkpoint.revert();
|
|
274
|
+
|
|
275
|
+
// Now we want to revert any/all remaining checkpoints, destroying any outstanding state updates.
|
|
276
|
+
// This needs to be done directly on the underlying fork as the guarded fork has been stopped.
|
|
277
|
+
await this.guardedMerkleTree.getUnderlyingFork().revertAllCheckpoints();
|
|
278
|
+
|
|
279
|
+
// We should now be in a position where the fork is in a clean state and no further updates can be made to it.
|
|
263
280
|
break;
|
|
264
281
|
}
|
|
282
|
+
|
|
283
|
+
// Roll back state to start of TX before proceeding to next TX
|
|
284
|
+
await checkpoint.revert();
|
|
265
285
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
266
286
|
this.log.warn(`Failed to process tx ${txHash.toString()}: ${errorMessage} ${err?.stack}`);
|
|
267
|
-
|
|
268
287
|
failed.push({ tx, error: err instanceof Error ? err : new Error(errorMessage) });
|
|
269
288
|
returns.push(new NestedProcessReturnValues([]));
|
|
270
289
|
} finally {
|
|
271
|
-
// Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was reverted
|
|
290
|
+
// Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
|
|
272
291
|
await checkpoint.commit();
|
|
273
292
|
// The tx-level contracts cache should not live on to the next tx
|
|
274
293
|
this.contractsDB.clearContractsForTx();
|
|
@@ -330,12 +349,12 @@ export class PublicProcessor implements Traceable {
|
|
|
330
349
|
// b) always had a txHandler with the same db passed to it as this.db, which updated the db in buildBaseRollupHints in this loop
|
|
331
350
|
// To see how this ^ happens, move back to one shared db in test_context and run orchestrator_multi_public_functions.test.ts
|
|
332
351
|
// The below is taken from buildBaseRollupHints:
|
|
333
|
-
await this.
|
|
352
|
+
await this.guardedMerkleTree.appendLeaves(
|
|
334
353
|
MerkleTreeId.NOTE_HASH_TREE,
|
|
335
354
|
padArrayEnd(processedTx.txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
|
|
336
355
|
);
|
|
337
356
|
try {
|
|
338
|
-
await this.
|
|
357
|
+
await this.guardedMerkleTree.batchInsert(
|
|
339
358
|
MerkleTreeId.NULLIFIER_TREE,
|
|
340
359
|
padArrayEnd(processedTx.txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX).map(n => n.toBuffer()),
|
|
341
360
|
NULLIFIER_SUBTREE_HEIGHT,
|
|
@@ -396,7 +415,7 @@ export class PublicProcessor implements Traceable {
|
|
|
396
415
|
const balanceSlot = await computeFeePayerBalanceStorageSlot(feePayer);
|
|
397
416
|
const leafSlot = await computeFeePayerBalanceLeafSlot(feePayer);
|
|
398
417
|
// This high-level db is used as a convenient helper. It could be done with the merkleTree directly.
|
|
399
|
-
const treesDB = new PublicTreesDB(this.
|
|
418
|
+
const treesDB = new PublicTreesDB(this.guardedMerkleTree);
|
|
400
419
|
|
|
401
420
|
this.log.debug(`Deducting ${txFee.toBigInt()} balance in Fee Juice for ${feePayer}`);
|
|
402
421
|
|
|
@@ -430,13 +449,9 @@ export class PublicProcessor implements Traceable {
|
|
|
430
449
|
this.globalVariables,
|
|
431
450
|
);
|
|
432
451
|
|
|
433
|
-
const siloedContractClassLogs = await tx.filterContractClassLogs(
|
|
434
|
-
tx.data.getNonEmptyContractClassLogsHashes(),
|
|
435
|
-
true,
|
|
436
|
-
);
|
|
437
|
-
|
|
438
452
|
this.metrics.recordClassRegistration(
|
|
439
|
-
...
|
|
453
|
+
...tx
|
|
454
|
+
.getContractClassLogs()
|
|
440
455
|
.filter(log => ContractClassRegisteredEvent.isContractClassRegisteredEvent(log))
|
|
441
456
|
.map(log => ContractClassRegisteredEvent.fromLog(log)),
|
|
442
457
|
);
|
|
@@ -474,13 +489,11 @@ export class PublicProcessor implements Traceable {
|
|
|
474
489
|
}
|
|
475
490
|
});
|
|
476
491
|
|
|
477
|
-
const
|
|
478
|
-
tx.
|
|
479
|
-
|
|
480
|
-
);
|
|
481
|
-
|
|
492
|
+
const contractClassLogs = revertCode.isOK()
|
|
493
|
+
? tx.getContractClassLogs()
|
|
494
|
+
: tx.getSplitContractClassLogs(false /* revertible */);
|
|
482
495
|
this.metrics.recordClassRegistration(
|
|
483
|
-
...
|
|
496
|
+
...contractClassLogs
|
|
484
497
|
.filter(log => ContractClassRegisteredEvent.isContractClassRegisteredEvent(log))
|
|
485
498
|
.map(log => ContractClassRegisteredEvent.fromLog(log)),
|
|
486
499
|
);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MAX_ENQUEUED_CALLS_PER_TX,
|
|
3
|
-
MAX_L2_GAS_PER_TX_PUBLIC_PORTION,
|
|
4
3
|
MAX_L2_TO_L1_MSGS_PER_TX,
|
|
5
4
|
MAX_NOTE_HASHES_PER_TX,
|
|
6
5
|
MAX_NULLIFIERS_PER_TX,
|
|
@@ -10,7 +9,14 @@ import {
|
|
|
10
9
|
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
11
10
|
import { Fr } from '@aztec/foundation/fields';
|
|
12
11
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
13
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
AvmAccumulatedData,
|
|
14
|
+
AvmAccumulatedDataArrayLengths,
|
|
15
|
+
AvmCircuitPublicInputs,
|
|
16
|
+
PublicDataWrite,
|
|
17
|
+
RevertCode,
|
|
18
|
+
clampGasSettingsForAVM,
|
|
19
|
+
} from '@aztec/stdlib/avm';
|
|
14
20
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
15
21
|
import type { SimulationError } from '@aztec/stdlib/errors';
|
|
16
22
|
import { computeTransactionFee } from '@aztec/stdlib/fees';
|
|
@@ -18,8 +24,9 @@ import { Gas, GasSettings } from '@aztec/stdlib/gas';
|
|
|
18
24
|
import {
|
|
19
25
|
PrivateToAvmAccumulatedData,
|
|
20
26
|
PrivateToAvmAccumulatedDataArrayLengths,
|
|
21
|
-
|
|
27
|
+
PrivateToPublicAccumulatedData,
|
|
22
28
|
PublicCallRequest,
|
|
29
|
+
PublicCallRequestArrayLengths,
|
|
23
30
|
countAccumulatedItems,
|
|
24
31
|
} from '@aztec/stdlib/kernel';
|
|
25
32
|
import { PublicLog } from '@aztec/stdlib/logs';
|
|
@@ -66,8 +73,10 @@ export class PublicTxContext {
|
|
|
66
73
|
private readonly startTreeSnapshots: TreeSnapshots,
|
|
67
74
|
private readonly globalVariables: GlobalVariables,
|
|
68
75
|
private readonly gasSettings: GasSettings,
|
|
76
|
+
private readonly clampedGasSettings: GasSettings,
|
|
69
77
|
private readonly gasUsedByPrivate: Gas,
|
|
70
78
|
private readonly gasAllocatedToPublic: Gas,
|
|
79
|
+
private readonly gasAllocatedToPublicTeardown: Gas,
|
|
71
80
|
private readonly setupCallRequests: PublicCallRequestWithCalldata[],
|
|
72
81
|
private readonly appLogicCallRequests: PublicCallRequestWithCalldata[],
|
|
73
82
|
private readonly teardownCallRequests: PublicCallRequestWithCalldata[],
|
|
@@ -105,7 +114,9 @@ export class PublicTxContext {
|
|
|
105
114
|
const gasSettings = tx.data.constants.txContext.gasSettings;
|
|
106
115
|
const gasUsedByPrivate = tx.data.gasUsed;
|
|
107
116
|
// Gas allocated to public is "whatever's left" after private, but with some max applied.
|
|
108
|
-
const
|
|
117
|
+
const clampedGasSettings = clampGasSettingsForAVM(gasSettings, gasUsedByPrivate);
|
|
118
|
+
const gasAllocatedToPublic = clampedGasSettings.gasLimits.sub(gasUsedByPrivate);
|
|
119
|
+
const gasAllocatedToPublicTeardown = clampedGasSettings.teardownGasLimits;
|
|
109
120
|
|
|
110
121
|
return new PublicTxContext(
|
|
111
122
|
await tx.getTxHash(),
|
|
@@ -113,8 +124,10 @@ export class PublicTxContext {
|
|
|
113
124
|
await txStateManager.getTreeSnapshots(),
|
|
114
125
|
globalVariables,
|
|
115
126
|
gasSettings,
|
|
127
|
+
clampedGasSettings,
|
|
116
128
|
gasUsedByPrivate,
|
|
117
129
|
gasAllocatedToPublic,
|
|
130
|
+
gasAllocatedToPublicTeardown,
|
|
118
131
|
getCallRequestsWithCalldataByPhase(tx, TxExecutionPhase.SETUP),
|
|
119
132
|
getCallRequestsWithCalldataByPhase(tx, TxExecutionPhase.APP_LOGIC),
|
|
120
133
|
getCallRequestsWithCalldataByPhase(tx, TxExecutionPhase.TEARDOWN),
|
|
@@ -210,7 +223,8 @@ export class PublicTxContext {
|
|
|
210
223
|
*/
|
|
211
224
|
getGasLeftAtPhase(phase: TxExecutionPhase): Gas {
|
|
212
225
|
if (phase === TxExecutionPhase.TEARDOWN) {
|
|
213
|
-
|
|
226
|
+
const gasLeftForPublicTeardown = this.gasAllocatedToPublicTeardown.sub(this.teardownGasUsed);
|
|
227
|
+
return gasLeftForPublicTeardown;
|
|
214
228
|
} else {
|
|
215
229
|
const gasLeftForPublic = this.gasAllocatedToPublic.sub(this.gasUsedByPublic);
|
|
216
230
|
return gasLeftForPublic;
|
|
@@ -356,19 +370,32 @@ export class PublicTxContext {
|
|
|
356
370
|
// This converts the private accumulated data to the avm accumulated data format.
|
|
357
371
|
const convertAccumulatedData = (from: PrivateToPublicAccumulatedData) =>
|
|
358
372
|
new PrivateToAvmAccumulatedData(from.noteHashes, from.nullifiers, from.l2ToL1Msgs);
|
|
359
|
-
const
|
|
373
|
+
const getPreviousAccumulatedDataArrayLengths = (from: PrivateToPublicAccumulatedData) =>
|
|
360
374
|
new PrivateToAvmAccumulatedDataArrayLengths(
|
|
361
375
|
countAccumulatedItems(from.noteHashes),
|
|
362
376
|
countAccumulatedItems(from.nullifiers),
|
|
363
377
|
countAccumulatedItems(from.l2ToL1Msgs),
|
|
364
378
|
);
|
|
379
|
+
const getAvmAccumulatedDataArrayLengths = (from: AvmAccumulatedData) =>
|
|
380
|
+
new AvmAccumulatedDataArrayLengths(
|
|
381
|
+
from.noteHashes.length,
|
|
382
|
+
from.nullifiers.length,
|
|
383
|
+
from.l2ToL1Msgs.length,
|
|
384
|
+
from.publicLogs.length,
|
|
385
|
+
from.publicDataWrites.length,
|
|
386
|
+
);
|
|
365
387
|
|
|
366
388
|
return new AvmCircuitPublicInputs(
|
|
367
389
|
this.globalVariables,
|
|
368
390
|
this.startTreeSnapshots,
|
|
369
391
|
/*startGasUsed=*/ this.gasUsedByPrivate,
|
|
370
|
-
this.
|
|
392
|
+
this.clampedGasSettings,
|
|
371
393
|
this.feePayer,
|
|
394
|
+
/*publicCallRequestArrayLengths=*/ new PublicCallRequestArrayLengths(
|
|
395
|
+
this.setupCallRequests.length,
|
|
396
|
+
this.appLogicCallRequests.length,
|
|
397
|
+
this.teardownCallRequests.length > 0,
|
|
398
|
+
),
|
|
372
399
|
/*publicSetupCallRequests=*/ padArrayEnd(
|
|
373
400
|
this.setupCallRequests.map(r => r.request),
|
|
374
401
|
PublicCallRequest.empty(),
|
|
@@ -382,12 +409,13 @@ export class PublicTxContext {
|
|
|
382
409
|
/*publicTeardownCallRequests=*/ this.teardownCallRequests.length > 0
|
|
383
410
|
? this.teardownCallRequests[0].request
|
|
384
411
|
: PublicCallRequest.empty(),
|
|
385
|
-
|
|
386
|
-
|
|
412
|
+
getPreviousAccumulatedDataArrayLengths(this.nonRevertibleAccumulatedDataFromPrivate),
|
|
413
|
+
getPreviousAccumulatedDataArrayLengths(this.revertibleAccumulatedDataFromPrivate),
|
|
387
414
|
convertAccumulatedData(this.nonRevertibleAccumulatedDataFromPrivate),
|
|
388
415
|
convertAccumulatedData(this.revertibleAccumulatedDataFromPrivate),
|
|
389
416
|
endTreeSnapshots,
|
|
390
417
|
this.getTotalGasUsed(),
|
|
418
|
+
getAvmAccumulatedDataArrayLengths(accumulatedData),
|
|
391
419
|
accumulatedData,
|
|
392
420
|
/*transactionFee=*/ this.getTransactionFeeUnsafe(),
|
|
393
421
|
/*isReverted=*/ !this.revertCode.isOK(),
|
|
@@ -444,13 +472,3 @@ class PhaseStateManager {
|
|
|
444
472
|
this.currentlyActiveStateManager = undefined;
|
|
445
473
|
}
|
|
446
474
|
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Apply L2 gas maximum.
|
|
450
|
-
*/
|
|
451
|
-
function applyMaxToAvailableGas(availableGas: Gas) {
|
|
452
|
-
return new Gas(
|
|
453
|
-
/*daGas=*/ availableGas.daGas,
|
|
454
|
-
/*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_TX_PUBLIC_PORTION),
|
|
455
|
-
);
|
|
456
|
-
}
|
|
@@ -121,9 +121,6 @@ export class PublicTxSimulator {
|
|
|
121
121
|
|
|
122
122
|
const revertCode = context.getFinalRevertCode();
|
|
123
123
|
|
|
124
|
-
if (!revertCode.isOK()) {
|
|
125
|
-
await tx.filterRevertedLogs();
|
|
126
|
-
}
|
|
127
124
|
// Commit contracts from this TX to the block-level cache and clear tx cache
|
|
128
125
|
// If the tx reverted, only commit non-revertible contracts
|
|
129
126
|
// NOTE: You can't create contracts in public, so this is only relevant for private-created contracts
|
|
@@ -82,6 +82,7 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface {
|
|
|
82
82
|
private readonly previousSideEffectArrayLengths: SideEffectArrayLengths = SideEffectArrayLengths.empty(),
|
|
83
83
|
/** We need to track the set of class IDs used, to enforce limits. */
|
|
84
84
|
private uniqueClassIds: UniqueClassIds = new UniqueClassIds(),
|
|
85
|
+
private writtenPublicDataSlots: Set<string> = new Set(),
|
|
85
86
|
) {
|
|
86
87
|
this.sideEffectCounter = startSideEffectCounter;
|
|
87
88
|
}
|
|
@@ -98,6 +99,7 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface {
|
|
|
98
99
|
this.previousSideEffectArrayLengths.publicLogs + this.publicLogs.length,
|
|
99
100
|
),
|
|
100
101
|
this.uniqueClassIds.fork(),
|
|
102
|
+
new Set(this.writtenPublicDataSlots),
|
|
101
103
|
);
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -111,6 +113,8 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface {
|
|
|
111
113
|
|
|
112
114
|
this.sideEffectCounter = forkedTrace.sideEffectCounter;
|
|
113
115
|
this.uniqueClassIds.acceptAndMerge(forkedTrace.uniqueClassIds);
|
|
116
|
+
// Accept even if reverted, since the user already paid for the writes
|
|
117
|
+
this.writtenPublicDataSlots = new Set(forkedTrace.writtenPublicDataSlots);
|
|
114
118
|
|
|
115
119
|
if (!reverted) {
|
|
116
120
|
this.publicDataWrites.push(...forkedTrace.publicDataWrites);
|
|
@@ -170,6 +174,15 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface {
|
|
|
170
174
|
`Traced public data write (address=${contractAddress}, slot=${slot}): value=${value} (counter=${this.sideEffectCounter}, isProtocol:${protocolWrite})`,
|
|
171
175
|
);
|
|
172
176
|
this.incrementSideEffectCounter();
|
|
177
|
+
this.writtenPublicDataSlots.add(this.computePublicDataSlotKey(contractAddress, slot));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private computePublicDataSlotKey(contractAddress: AztecAddress, slot: Fr): string {
|
|
181
|
+
return `${contractAddress.toString()}:${slot.toString()}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public isStorageCold(contractAddress: AztecAddress, slot: Fr): boolean {
|
|
185
|
+
return !this.writtenPublicDataSlots.has(this.computePublicDataSlotKey(contractAddress, slot));
|
|
173
186
|
}
|
|
174
187
|
|
|
175
188
|
public traceNewNoteHash(noteHash: Fr) {
|
|
@@ -12,6 +12,7 @@ export interface PublicSideEffectTraceInterface {
|
|
|
12
12
|
value: Fr,
|
|
13
13
|
protocolWrite: boolean,
|
|
14
14
|
): Promise<void>;
|
|
15
|
+
isStorageCold(contractAddress: AztecAddress, slot: Fr): boolean;
|
|
15
16
|
traceNewNoteHash(uniqueNoteHash: Fr): void;
|
|
16
17
|
getNoteHashCount(): number;
|
|
17
18
|
traceNewNullifier(siloedNullifier: Fr): void;
|
|
@@ -148,6 +148,10 @@ export class PublicPersistableStateManager {
|
|
|
148
148
|
await this.trace.tracePublicStorageWrite(contractAddress, slot, value, protocolWrite);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
public isStorageCold(contractAddress: AztecAddress, slot: Fr): boolean {
|
|
152
|
+
return this.trace.isStorageCold(contractAddress, slot);
|
|
153
|
+
}
|
|
154
|
+
|
|
151
155
|
/**
|
|
152
156
|
* Read from public storage.
|
|
153
157
|
*
|