@aztec-labs/simulator 6.0.0-nightly.20260829

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.
Files changed (215) hide show
  1. package/README.md +57 -0
  2. package/dest/client.d.ts +6 -0
  3. package/dest/client.d.ts.map +1 -0
  4. package/dest/client.js +3 -0
  5. package/dest/common/errors.d.ts +51 -0
  6. package/dest/common/errors.d.ts.map +1 -0
  7. package/dest/common/errors.js +154 -0
  8. package/dest/common/index.d.ts +3 -0
  9. package/dest/common/index.d.ts.map +1 -0
  10. package/dest/common/index.js +2 -0
  11. package/dest/common/stats/index.d.ts +2 -0
  12. package/dest/common/stats/index.d.ts.map +1 -0
  13. package/dest/common/stats/index.js +1 -0
  14. package/dest/common/stats/stats.d.ts +4 -0
  15. package/dest/common/stats/stats.d.ts.map +1 -0
  16. package/dest/common/stats/stats.js +10 -0
  17. package/dest/private/acvm/acvm.d.ts +43 -0
  18. package/dest/private/acvm/acvm.d.ts.map +1 -0
  19. package/dest/private/acvm/acvm.js +65 -0
  20. package/dest/private/acvm/acvm_types.d.ts +10 -0
  21. package/dest/private/acvm/acvm_types.d.ts.map +1 -0
  22. package/dest/private/acvm/acvm_types.js +3 -0
  23. package/dest/private/acvm/deserialize.d.ts +35 -0
  24. package/dest/private/acvm/deserialize.d.ts.map +1 -0
  25. package/dest/private/acvm/deserialize.js +50 -0
  26. package/dest/private/acvm/index.d.ts +5 -0
  27. package/dest/private/acvm/index.d.ts.map +1 -0
  28. package/dest/private/acvm/index.js +4 -0
  29. package/dest/private/acvm/serialize.d.ts +41 -0
  30. package/dest/private/acvm/serialize.d.ts.map +1 -0
  31. package/dest/private/acvm/serialize.js +99 -0
  32. package/dest/private/acvm_native.d.ts +41 -0
  33. package/dest/private/acvm_native.d.ts.map +1 -0
  34. package/dest/private/acvm_native.js +147 -0
  35. package/dest/private/acvm_wasm.d.ts +16 -0
  36. package/dest/private/acvm_wasm.d.ts.map +1 -0
  37. package/dest/private/acvm_wasm.js +67 -0
  38. package/dest/private/acvm_wasm_with_blobs.d.ts +20 -0
  39. package/dest/private/acvm_wasm_with_blobs.d.ts.map +1 -0
  40. package/dest/private/acvm_wasm_with_blobs.js +35 -0
  41. package/dest/private/circuit_simulator.d.ts +35 -0
  42. package/dest/private/circuit_simulator.d.ts.map +1 -0
  43. package/dest/private/circuit_simulator.js +43 -0
  44. package/dest/private/factory.d.ts +12 -0
  45. package/dest/private/factory.d.ts.map +1 -0
  46. package/dest/private/factory.js +30 -0
  47. package/dest/public/avm/testing/account_proof_fetcher.d.ts +2 -0
  48. package/dest/public/avm/testing/account_proof_fetcher.d.ts.map +1 -0
  49. package/dest/public/avm/testing/account_proof_fetcher.js +152 -0
  50. package/dest/public/avm/testing/base_avm_simulation_tester.d.ts +39 -0
  51. package/dest/public/avm/testing/base_avm_simulation_tester.d.ts.map +1 -0
  52. package/dest/public/avm/testing/base_avm_simulation_tester.js +121 -0
  53. package/dest/public/avm/testing/utils.d.ts +32 -0
  54. package/dest/public/avm/testing/utils.d.ts.map +1 -0
  55. package/dest/public/avm/testing/utils.js +66 -0
  56. package/dest/public/avm_simulator.d.ts +28 -0
  57. package/dest/public/avm_simulator.d.ts.map +1 -0
  58. package/dest/public/avm_simulator.js +4 -0
  59. package/dest/public/avm_simulator_pool.d.ts +84 -0
  60. package/dest/public/avm_simulator_pool.d.ts.map +1 -0
  61. package/dest/public/avm_simulator_pool.js +311 -0
  62. package/dest/public/cdb_ipc_server.d.ts +40 -0
  63. package/dest/public/cdb_ipc_server.d.ts.map +1 -0
  64. package/dest/public/cdb_ipc_server.js +153 -0
  65. package/dest/public/contracts_db_checkpoint.d.ts +16 -0
  66. package/dest/public/contracts_db_checkpoint.d.ts.map +1 -0
  67. package/dest/public/contracts_db_checkpoint.js +30 -0
  68. package/dest/public/db_interfaces.d.ts +68 -0
  69. package/dest/public/db_interfaces.d.ts.map +1 -0
  70. package/dest/public/db_interfaces.js +3 -0
  71. package/dest/public/executor_metrics.d.ts +21 -0
  72. package/dest/public/executor_metrics.d.ts.map +1 -0
  73. package/dest/public/executor_metrics.js +64 -0
  74. package/dest/public/executor_metrics_interface.d.ts +10 -0
  75. package/dest/public/executor_metrics_interface.d.ts.map +1 -0
  76. package/dest/public/executor_metrics_interface.js +1 -0
  77. package/dest/public/fixtures/amm_test.d.ts +10 -0
  78. package/dest/public/fixtures/amm_test.d.ts.map +1 -0
  79. package/dest/public/fixtures/amm_test.js +213 -0
  80. package/dest/public/fixtures/bulk_test.d.ts +6 -0
  81. package/dest/public/fixtures/bulk_test.d.ts.map +1 -0
  82. package/dest/public/fixtures/bulk_test.js +289 -0
  83. package/dest/public/fixtures/custom_bytecode_tester.d.ts +34 -0
  84. package/dest/public/fixtures/custom_bytecode_tester.d.ts.map +1 -0
  85. package/dest/public/fixtures/custom_bytecode_tester.js +53 -0
  86. package/dest/public/fixtures/index.d.ts +10 -0
  87. package/dest/public/fixtures/index.d.ts.map +1 -0
  88. package/dest/public/fixtures/index.js +9 -0
  89. package/dest/public/fixtures/public_processor_test_env.d.ts +30 -0
  90. package/dest/public/fixtures/public_processor_test_env.d.ts.map +1 -0
  91. package/dest/public/fixtures/public_processor_test_env.js +73 -0
  92. package/dest/public/fixtures/public_tx_simulation_tester.d.ts +77 -0
  93. package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +1 -0
  94. package/dest/public/fixtures/public_tx_simulation_tester.js +176 -0
  95. package/dest/public/fixtures/simple_contract_data_source.d.ts +36 -0
  96. package/dest/public/fixtures/simple_contract_data_source.d.ts.map +1 -0
  97. package/dest/public/fixtures/simple_contract_data_source.js +95 -0
  98. package/dest/public/fixtures/token_test.d.ts +12 -0
  99. package/dest/public/fixtures/token_test.d.ts.map +1 -0
  100. package/dest/public/fixtures/token_test.js +96 -0
  101. package/dest/public/fixtures/utils.d.ts +26 -0
  102. package/dest/public/fixtures/utils.d.ts.map +1 -0
  103. package/dest/public/fixtures/utils.js +170 -0
  104. package/dest/public/fuzzing/avm_fuzzer_simulator.d.ts +65 -0
  105. package/dest/public/fuzzing/avm_fuzzer_simulator.d.ts.map +1 -0
  106. package/dest/public/fuzzing/avm_fuzzer_simulator.js +181 -0
  107. package/dest/public/fuzzing/avm_simulator_bin.d.ts +2 -0
  108. package/dest/public/fuzzing/avm_simulator_bin.d.ts.map +1 -0
  109. package/dest/public/fuzzing/avm_simulator_bin.js +108 -0
  110. package/dest/public/index.d.ts +10 -0
  111. package/dest/public/index.d.ts.map +1 -0
  112. package/dest/public/index.js +6 -0
  113. package/dest/public/public_db_sources.d.ts +83 -0
  114. package/dest/public/public_db_sources.d.ts.map +1 -0
  115. package/dest/public/public_db_sources.js +297 -0
  116. package/dest/public/public_errors.d.ts +12 -0
  117. package/dest/public/public_errors.d.ts.map +1 -0
  118. package/dest/public/public_errors.js +13 -0
  119. package/dest/public/public_processor/guarded_merkle_tree.d.ts +52 -0
  120. package/dest/public/public_processor/guarded_merkle_tree.d.ts.map +1 -0
  121. package/dest/public/public_processor/guarded_merkle_tree.js +116 -0
  122. package/dest/public/public_processor/public_processor.d.ts +71 -0
  123. package/dest/public/public_processor/public_processor.d.ts.map +1 -0
  124. package/dest/public/public_processor/public_processor.js +858 -0
  125. package/dest/public/public_processor/public_processor_metrics.d.ts +30 -0
  126. package/dest/public/public_processor/public_processor_metrics.d.ts.map +1 -0
  127. package/dest/public/public_processor/public_processor_metrics.js +116 -0
  128. package/dest/public/public_tx_simulator/dumping_public_tx_simulator.d.ts +17 -0
  129. package/dest/public/public_tx_simulator/dumping_public_tx_simulator.d.ts.map +1 -0
  130. package/dest/public/public_tx_simulator/dumping_public_tx_simulator.js +39 -0
  131. package/dest/public/public_tx_simulator/factories.d.ts +14 -0
  132. package/dest/public/public_tx_simulator/factories.d.ts.map +1 -0
  133. package/dest/public/public_tx_simulator/factories.js +28 -0
  134. package/dest/public/public_tx_simulator/index.d.ts +6 -0
  135. package/dest/public/public_tx_simulator/index.d.ts.map +1 -0
  136. package/dest/public/public_tx_simulator/index.js +3 -0
  137. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts +53 -0
  138. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts.map +1 -0
  139. package/dest/public/public_tx_simulator/public_tx_simulator.js +127 -0
  140. package/dest/public/public_tx_simulator/public_tx_simulator_base.d.ts +24 -0
  141. package/dest/public/public_tx_simulator/public_tx_simulator_base.d.ts.map +1 -0
  142. package/dest/public/public_tx_simulator/public_tx_simulator_base.js +31 -0
  143. package/dest/public/public_tx_simulator/public_tx_simulator_interface.d.ts +32 -0
  144. package/dest/public/public_tx_simulator/public_tx_simulator_interface.d.ts.map +1 -0
  145. package/dest/public/public_tx_simulator/public_tx_simulator_interface.js +1 -0
  146. package/dest/public/side_effect_errors.d.ts +44 -0
  147. package/dest/public/side_effect_errors.d.ts.map +1 -0
  148. package/dest/public/side_effect_errors.js +75 -0
  149. package/dest/public/test_executor_metrics.d.ts +56 -0
  150. package/dest/public/test_executor_metrics.d.ts.map +1 -0
  151. package/dest/public/test_executor_metrics.js +305 -0
  152. package/dest/public/unique_class_ids.d.ts +37 -0
  153. package/dest/public/unique_class_ids.d.ts.map +1 -0
  154. package/dest/public/unique_class_ids.js +61 -0
  155. package/dest/public/utils.d.ts +3 -0
  156. package/dest/public/utils.d.ts.map +1 -0
  157. package/dest/public/utils.js +18 -0
  158. package/dest/server.d.ts +7 -0
  159. package/dest/server.d.ts.map +1 -0
  160. package/dest/server.js +5 -0
  161. package/package.json +109 -0
  162. package/src/client.ts +5 -0
  163. package/src/common/errors.ts +219 -0
  164. package/src/common/index.ts +2 -0
  165. package/src/common/stats/index.ts +1 -0
  166. package/src/common/stats/stats.ts +20 -0
  167. package/src/private/acvm/acvm.ts +114 -0
  168. package/src/private/acvm/acvm_types.ts +11 -0
  169. package/src/private/acvm/deserialize.ts +58 -0
  170. package/src/private/acvm/index.ts +4 -0
  171. package/src/private/acvm/serialize.ts +123 -0
  172. package/src/private/acvm_native.ts +200 -0
  173. package/src/private/acvm_wasm.ts +80 -0
  174. package/src/private/acvm_wasm_with_blobs.ts +55 -0
  175. package/src/private/circuit_simulator.ts +91 -0
  176. package/src/private/factory.ts +40 -0
  177. package/src/public/avm/testing/account_proof.json +553 -0
  178. package/src/public/avm/testing/account_proof_fetcher.ts +166 -0
  179. package/src/public/avm/testing/base_avm_simulation_tester.ts +162 -0
  180. package/src/public/avm/testing/utils.ts +114 -0
  181. package/src/public/avm_simulator.ts +29 -0
  182. package/src/public/avm_simulator_pool.ts +355 -0
  183. package/src/public/cdb_ipc_server.ts +198 -0
  184. package/src/public/contracts_db_checkpoint.ts +41 -0
  185. package/src/public/db_interfaces.ts +76 -0
  186. package/src/public/executor_metrics.ts +102 -0
  187. package/src/public/executor_metrics_interface.ts +20 -0
  188. package/src/public/fixtures/amm_test.ts +331 -0
  189. package/src/public/fixtures/bulk_test.ts +194 -0
  190. package/src/public/fixtures/custom_bytecode_tester.ts +83 -0
  191. package/src/public/fixtures/index.ts +13 -0
  192. package/src/public/fixtures/public_processor_test_env.ts +88 -0
  193. package/src/public/fixtures/public_tx_simulation_tester.ts +315 -0
  194. package/src/public/fixtures/simple_contract_data_source.ts +122 -0
  195. package/src/public/fixtures/token_test.ts +148 -0
  196. package/src/public/fixtures/utils.ts +269 -0
  197. package/src/public/fuzzing/avm_fuzzer_simulator.ts +302 -0
  198. package/src/public/fuzzing/avm_simulator_bin.ts +156 -0
  199. package/src/public/index.ts +16 -0
  200. package/src/public/public_db_sources.ts +405 -0
  201. package/src/public/public_errors.ts +14 -0
  202. package/src/public/public_processor/guarded_merkle_tree.ts +161 -0
  203. package/src/public/public_processor/public_processor.ts +657 -0
  204. package/src/public/public_processor/public_processor_metrics.ts +138 -0
  205. package/src/public/public_tx_simulator/dumping_public_tx_simulator.ts +66 -0
  206. package/src/public/public_tx_simulator/factories.ts +61 -0
  207. package/src/public/public_tx_simulator/index.ts +8 -0
  208. package/src/public/public_tx_simulator/public_tx_simulator.ts +190 -0
  209. package/src/public/public_tx_simulator/public_tx_simulator_base.ts +37 -0
  210. package/src/public/public_tx_simulator/public_tx_simulator_interface.ts +33 -0
  211. package/src/public/side_effect_errors.ts +96 -0
  212. package/src/public/test_executor_metrics.ts +399 -0
  213. package/src/public/unique_class_ids.ts +79 -0
  214. package/src/public/utils.ts +16 -0
  215. package/src/server.ts +6 -0
@@ -0,0 +1,657 @@
1
+ import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, NULLIFIER_SUBTREE_HEIGHT } from '@aztec-labs/constants';
2
+ import { padArrayEnd } from '@aztec-labs/foundation/collection';
3
+ import { Fr } from '@aztec-labs/foundation/curves/bn254';
4
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec-labs/foundation/log';
5
+ import { sleep } from '@aztec-labs/foundation/sleep';
6
+ import { DateProvider, Timer, elapsed, execWithSignal } from '@aztec-labs/foundation/timer';
7
+ import { ProtocolContractAddress } from '@aztec-labs/protocol-contracts';
8
+ import { ContractClassPublishedEvent } from '@aztec-labs/protocol-contracts/class-registry';
9
+ import {
10
+ computeFeePayerBalanceLeafSlot,
11
+ computeFeePayerBalanceStorageSlot,
12
+ } from '@aztec-labs/protocol-contracts/fee-juice';
13
+ import {
14
+ AvmCircuitInputs,
15
+ AvmCircuitPublicInputs,
16
+ AvmExecutionHints,
17
+ type AvmProvingRequest,
18
+ PublicDataWrite,
19
+ PublicSimulatorConfig,
20
+ } from '@aztec-labs/stdlib/avm';
21
+ import type { AztecAddress } from '@aztec-labs/stdlib/aztec-address';
22
+ import type { ContractDataSource } from '@aztec-labs/stdlib/contract';
23
+ import { computeTransactionFee } from '@aztec-labs/stdlib/fees';
24
+ import { Gas } from '@aztec-labs/stdlib/gas';
25
+ import type {
26
+ MerkleTreeWriteOperations,
27
+ PublicProcessorLimits,
28
+ PublicProcessorValidator,
29
+ SequencerConfig,
30
+ } from '@aztec-labs/stdlib/interfaces/server';
31
+ import { type DebugLog, type DebugLogStore, NullDebugLogStore } from '@aztec-labs/stdlib/logs';
32
+ import { ProvingRequestType } from '@aztec-labs/stdlib/proofs';
33
+ import { MerkleTreeId } from '@aztec-labs/stdlib/trees';
34
+ import {
35
+ type FailedTx,
36
+ GlobalVariables,
37
+ NestedProcessReturnValues,
38
+ type ProcessedTx,
39
+ StateReference,
40
+ Tx,
41
+ makeProcessedTxFromPrivateOnlyTx,
42
+ makeProcessedTxFromTxWithPublicCalls,
43
+ } from '@aztec-labs/stdlib/tx';
44
+ import {
45
+ Attributes,
46
+ type TelemetryClient,
47
+ type Traceable,
48
+ type Tracer,
49
+ getTelemetryClient,
50
+ trackSpan,
51
+ } from '@aztec-labs/telemetry-client';
52
+ import { ForkCheckpoint } from '@aztec-labs/world-state/native';
53
+ import { AssertionError } from 'assert';
54
+
55
+ import type { AvmSimulator } from '../avm_simulator.js';
56
+ import { PublicContractsDB, PublicTreesDB } from '../public_db_sources.js';
57
+ import {
58
+ type PublicTxSimulatorConfig,
59
+ type PublicTxSimulatorInterface,
60
+ TelemetryPublicTxSimulator,
61
+ } from '../public_tx_simulator/index.js';
62
+ import { GuardedMerkleTreeOperations } from './guarded_merkle_tree.js';
63
+ import { PublicProcessorMetrics } from './public_processor_metrics.js';
64
+
65
+ /**
66
+ * Creates new instances of PublicProcessor given the provided merkle tree db and contract data source.
67
+ */
68
+ export class PublicProcessorFactory {
69
+ private log: Logger;
70
+ constructor(
71
+ private contractDataSource: ContractDataSource,
72
+ private avmSimulator: AvmSimulator,
73
+ private dateProvider: DateProvider = new DateProvider(),
74
+ protected telemetryClient: TelemetryClient = getTelemetryClient(),
75
+ bindings?: LoggerBindings,
76
+ ) {
77
+ this.log = createLogger('simulator:public-processor-factory', bindings);
78
+ }
79
+
80
+ /**
81
+ * Creates a new instance of a PublicProcessor. The simulator it drives reads contract data from the fork's
82
+ * contracts DB, scoped by the fork id so AVM requests route to the right state.
83
+ *
84
+ * @param globalVariables - The global variables for the block being processed.
85
+ * @param contractsDB - Optional pre-populated contracts DB; a fresh one is constructed if omitted.
86
+ * @returns A new instance of a PublicProcessor.
87
+ */
88
+ public create(
89
+ merkleTree: MerkleTreeWriteOperations,
90
+ globalVariables: GlobalVariables,
91
+ config: PublicSimulatorConfig,
92
+ contractsDB: PublicContractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings()),
93
+ ): PublicProcessor {
94
+ const forkId = merkleTree.getRevision().forkId;
95
+
96
+ const guardedFork = new GuardedMerkleTreeOperations(merkleTree);
97
+ const publicTxSimulator = this.createPublicTxSimulator(forkId, globalVariables, contractsDB, config);
98
+
99
+ return new PublicProcessor(
100
+ globalVariables,
101
+ guardedFork,
102
+ contractsDB,
103
+ publicTxSimulator,
104
+ this.dateProvider,
105
+ this.telemetryClient,
106
+ createLogger('simulator:public-processor', this.log.getBindings()),
107
+ );
108
+ }
109
+
110
+ protected createPublicTxSimulator(
111
+ forkId: number,
112
+ globalVariables: GlobalVariables,
113
+ contractsDB: PublicContractsDB,
114
+ config?: Partial<PublicTxSimulatorConfig>,
115
+ ): PublicTxSimulatorInterface {
116
+ return new TelemetryPublicTxSimulator(
117
+ this.avmSimulator,
118
+ globalVariables,
119
+ contractsDB,
120
+ forkId,
121
+ this.telemetryClient,
122
+ config,
123
+ this.log.getBindings(),
124
+ );
125
+ }
126
+ }
127
+
128
+ class PublicProcessorTimeoutError extends Error {
129
+ constructor(message: string = 'Timed out while processing tx') {
130
+ super(message);
131
+ this.name = 'PublicProcessorTimeoutError';
132
+ }
133
+ }
134
+
135
+ class PublicProcessorAbortError extends Error {
136
+ constructor(message: string = 'Aborted while processing tx') {
137
+ super(message);
138
+ this.name = 'PublicProcessorAbortError';
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Converts Txs lifted from the P2P module into ProcessedTx objects by executing
144
+ * any public function calls in them. Txs with private calls only are unaffected.
145
+ */
146
+ export class PublicProcessor implements Traceable {
147
+ private metrics: PublicProcessorMetrics;
148
+ constructor(
149
+ protected globalVariables: GlobalVariables,
150
+ private guardedMerkleTree: GuardedMerkleTreeOperations,
151
+ protected contractsDB: PublicContractsDB,
152
+ protected publicTxSimulator: PublicTxSimulatorInterface,
153
+ private dateProvider: DateProvider,
154
+ telemetryClient: TelemetryClient = getTelemetryClient(),
155
+ private log: Logger,
156
+ private opts: Pick<SequencerConfig, 'fakeProcessingDelayPerTxMs' | 'fakeThrowAfterProcessingTxCount'> = {},
157
+ private debugLogStore: DebugLogStore = new NullDebugLogStore(),
158
+ ) {
159
+ this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor');
160
+ }
161
+
162
+ get tracer(): Tracer {
163
+ return this.metrics.tracer;
164
+ }
165
+
166
+ /**
167
+ * Run each tx through the public circuit and the public kernel circuit if needed.
168
+ * @param txs - Txs to process.
169
+ * @param limits - Limits for processing the txs.
170
+ * @param validator - Pre-process validator and nullifier cache to use for processing the txs.
171
+ * @returns The list of processed txs with their circuit simulation outputs.
172
+ */
173
+ public async process(
174
+ txs: Iterable<Tx> | AsyncIterable<Tx>,
175
+ limits: PublicProcessorLimits = {},
176
+ validator: PublicProcessorValidator = {},
177
+ ): Promise<[ProcessedTx[], FailedTx[], Tx[], NestedProcessReturnValues[], DebugLog[]]> {
178
+ const { maxTransactions, deadline, maxBlockGas, maxBlobFields, isBuildingProposal, signal } = limits;
179
+ const { preprocessValidator, nullifierCache } = validator;
180
+ const result: ProcessedTx[] = [];
181
+ const usedTxs: Tx[] = [];
182
+ const failed: FailedTx[] = [];
183
+ const debugLogs: DebugLog[] = [];
184
+ const timer = new Timer();
185
+
186
+ let totalSizeInBytes = 0;
187
+ let returns: NestedProcessReturnValues[] = [];
188
+ let totalPublicGas = new Gas(0, 0);
189
+ let totalBlockGas = new Gas(0, 0);
190
+ let totalBlobFields = 0;
191
+ let silentlySkippedCount = 0;
192
+ let totalSilentlySkippedDurationMs = 0;
193
+
194
+ for await (const tx of txs) {
195
+ // Only process up to the max tx limit
196
+ if (maxTransactions !== undefined && result.length >= maxTransactions) {
197
+ this.log.debug(`Stopping tx processing due to reaching the max tx limit.`);
198
+ break;
199
+ }
200
+
201
+ // Bail if we've hit the deadline or have been interrupted.
202
+ if (deadline && this.dateProvider.now() > +deadline) {
203
+ this.log.warn(`Stopping tx processing due to timeout.`);
204
+ break;
205
+ }
206
+ if (signal?.aborted) {
207
+ this.log.warn(`Stopping tx processing due to abort signal.`);
208
+ break;
209
+ }
210
+
211
+ const txHash = tx.getTxHash().toString();
212
+
213
+ // Skip this tx if its estimated blob fields would exceed the limit.
214
+ // Only done during proposal building: during re-execution we must process the exact txs from the proposal.
215
+ const txBlobFields = tx.getPrivateTxEffectsSizeInFields();
216
+ if (isBuildingProposal && maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
217
+ this.log.warn(
218
+ `Skipping tx ${txHash} with ${txBlobFields} fields from private side effects due to blob fields limit`,
219
+ { txHash, txBlobFields, totalBlobFields, maxBlobFields },
220
+ );
221
+ continue;
222
+ }
223
+
224
+ // Skip this tx if its gas limit would exceed the block gas limit (either da or l2).
225
+ // Only done during proposal building: during re-execution we must process the exact txs from the proposal.
226
+ const txGasLimit = tx.data.constants.txContext.gasSettings.gasLimits;
227
+ if (isBuildingProposal && maxBlockGas !== undefined && totalBlockGas.add(txGasLimit).gtAny(maxBlockGas)) {
228
+ this.log.warn(`Skipping processing of tx ${txHash} due to block gas limit`, {
229
+ txHash,
230
+ txGasLimit,
231
+ totalBlockGas,
232
+ maxBlockGas,
233
+ });
234
+ continue;
235
+ }
236
+
237
+ // We validate the tx before processing it, to avoid unnecessary work.
238
+ if (preprocessValidator) {
239
+ const result = await preprocessValidator.validateTx(tx);
240
+ const txHash = tx.getTxHash();
241
+ if (result.result === 'invalid') {
242
+ const reason = result.reason.join(', ');
243
+ this.log.debug(`Rejecting tx ${txHash.toString()} due to pre-process validation fail: ${reason}`);
244
+ failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) });
245
+ returns.push(new NestedProcessReturnValues([]));
246
+ continue;
247
+ } else {
248
+ this.log.trace(`Tx ${txHash.toString()} is valid before processing.`);
249
+ }
250
+ }
251
+
252
+ // We checkpoint the transaction here, then within the try/catch we
253
+ // 1. Revert the checkpoint if the tx fails or needs to be discarded for any reason
254
+ // 2. Commit the transaction in the finally block. Note that by using the ForkCheckpoint lifecycle only the first commit/revert takes effect
255
+ // 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.
256
+ // Note: We use the underlying fork here not the guarded one, this ensures that it's not impacted by stopping the guarded version
257
+ const checkpoint = await ForkCheckpoint.new(this.guardedMerkleTree.getUnderlyingFork());
258
+ const startStateReference = await this.guardedMerkleTree.getUnderlyingFork().getStateReference();
259
+ this.contractsDB.createCheckpoint();
260
+
261
+ try {
262
+ const [txProcessingTimeMs, [processedTx, returnValues, txDebugLogs]] = await elapsed(() =>
263
+ this.processTx(tx, deadline, signal),
264
+ );
265
+
266
+ // Inject a fake processing failure after N txs if requested
267
+ const fakeThrowAfter = this.opts.fakeThrowAfterProcessingTxCount;
268
+ if (fakeThrowAfter !== undefined && result.length + failed.length + 1 >= fakeThrowAfter) {
269
+ throw new Error(`Fake error after processing ${fakeThrowAfter} txs`);
270
+ }
271
+
272
+ const txBlobFields = processedTx.txEffect.getNumBlobFields();
273
+ const txSize = txBlobFields * Fr.SIZE_IN_BYTES;
274
+
275
+ // If the actual blob fields of this tx would exceed the limit, skip it.
276
+ // Note: maxBlobFields already accounts for block end blob fields and previous blocks in checkpoint.
277
+ if (maxBlobFields !== undefined && totalBlobFields + txBlobFields > maxBlobFields) {
278
+ this.log.debug(
279
+ `Skipping processed tx ${txHash} with ${txBlobFields} blob fields due to max blob fields limit.`,
280
+ {
281
+ txHash,
282
+ txBlobFields,
283
+ totalBlobFields,
284
+ maxBlobFields,
285
+ txProcessingTimeMs,
286
+ },
287
+ );
288
+ silentlySkippedCount += 1;
289
+ totalSilentlySkippedDurationMs += txProcessingTimeMs;
290
+ this.metrics.recordSilentlySkipped(txProcessingTimeMs);
291
+ // Need to revert the checkpoint here and don't go any further
292
+ await checkpoint.revert();
293
+ this.contractsDB.revertCheckpoint();
294
+ continue;
295
+ }
296
+
297
+ // During re-execution, check if the actual gas used by this tx would push the block over the gas limit.
298
+ // Unlike the proposal-building check (which uses declared gas limits pessimistically before processing),
299
+ // this uses actual gas and stops processing when the limit is exceeded.
300
+ if (
301
+ !isBuildingProposal &&
302
+ maxBlockGas !== undefined &&
303
+ totalBlockGas.add(processedTx.gasUsed.totalGas).gtAny(maxBlockGas)
304
+ ) {
305
+ this.log.warn(`Stopping re-execution since tx ${txHash} would push block gas over limit`, {
306
+ txHash,
307
+ txGas: processedTx.gasUsed.totalGas,
308
+ totalBlockGas,
309
+ maxBlockGas,
310
+ });
311
+ await checkpoint.revert();
312
+ this.contractsDB.revertCheckpoint();
313
+ break;
314
+ }
315
+
316
+ // FIXME(fcarreiro): it's ugly to have to notify the validator of nullifiers.
317
+ // I'd rather pass the validators the processedTx as well and let them deal with it.
318
+ nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer()));
319
+ result.push(processedTx);
320
+ usedTxs.push(tx);
321
+ returns = returns.concat(returnValues);
322
+ debugLogs.push(...txDebugLogs);
323
+
324
+ this.debugLogStore.storeLogs(processedTx.hash.toString(), txDebugLogs);
325
+
326
+ totalPublicGas = totalPublicGas.add(processedTx.gasUsed.publicGas);
327
+ totalBlockGas = totalBlockGas.add(processedTx.gasUsed.totalGas);
328
+ totalSizeInBytes += txSize;
329
+ totalBlobFields += txBlobFields;
330
+
331
+ // Commit the tx-level contracts checkpoint on success
332
+ this.contractsDB.commitCheckpoint();
333
+ } catch (err: any) {
334
+ if (err?.name === 'PublicProcessorTimeoutError' || err?.name === 'PublicProcessorAbortError') {
335
+ this.log.warn(
336
+ `Stopping tx processing due to ${err.name === 'PublicProcessorTimeoutError' ? 'timeout' : 'abort'}.`,
337
+ );
338
+ // We hit the transaction execution deadline or an external abort signal.
339
+ // There may still be a simulation in progress.
340
+ // Signal cancellation AND WAIT for the simulation to actually stop. Cancellation is
341
+ // cooperative: the simulator may be mid slow-operation and won't observe the flag until it
342
+ // completes, and without waiting we'd revert checkpoints while it's still writing to state.
343
+ await this.publicTxSimulator.cancel?.();
344
+
345
+ // Now stop the guarded fork to prevent any further TS-side access to the world state.
346
+ await this.guardedMerkleTree.stop();
347
+
348
+ // We now know there can't be any further access to world state. The fork is in a state where there is:
349
+ // 1. At least one outstanding checkpoint that has not been committed (the one created before we processed the tx).
350
+ // 2. Possible state updates on that checkpoint or any others created during execution.
351
+
352
+ // Revert all checkpoints at or above this checkpoint's depth (inclusive), destroying any outstanding state
353
+ // updates from this tx and any nested checkpoints created during execution. This preserves any checkpoints
354
+ // created by callers below our depth.
355
+ await checkpoint.revertToCheckpoint();
356
+
357
+ // Revert any contracts added to the DB for the tx.
358
+ this.contractsDB.revertCheckpoint();
359
+
360
+ // Ensure we're at the same state as when we started processing this tx.
361
+ await this.checkWorldStateUnchanged(startStateReference, txHash, err);
362
+
363
+ // We should now be in a position where the fork is in a clean state and no further updates can be made to it.
364
+ break;
365
+ }
366
+
367
+ // Roll back state to start of TX before proceeding to next TX.
368
+ // Reverts all checkpoints at or above this checkpoint's depth, preserving any caller checkpoints below.
369
+ await checkpoint.revertToCheckpoint();
370
+ this.contractsDB.revertCheckpoint();
371
+ const errorMessage = err instanceof Error || err instanceof AssertionError ? err.message : 'Unknown error';
372
+ this.log.warn(`Failed to process tx ${txHash.toString()}: ${errorMessage} ${err?.stack}`);
373
+ failed.push({ tx, error: err instanceof Error ? err : new Error(errorMessage) });
374
+ returns.push(new NestedProcessReturnValues([]));
375
+
376
+ // Ensure we're at the same state as when we started processing this tx.
377
+ await this.checkWorldStateUnchanged(startStateReference, txHash, err);
378
+ } finally {
379
+ // Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was previously reverted
380
+ await checkpoint.commit();
381
+ }
382
+ }
383
+
384
+ const duration = timer.s();
385
+ const rate = duration > 0 ? totalPublicGas.l2Gas / duration : 0;
386
+ this.metrics.recordAllTxs(totalPublicGas, rate);
387
+
388
+ const silentlySkippedDurationMs = Math.round(totalSilentlySkippedDurationMs);
389
+ this.log.info(
390
+ `Processed ${result.length} successful txs and ${failed.length} failed txs ` +
391
+ `(${silentlySkippedCount} silently skipped, ${silentlySkippedDurationMs}ms wasted) ` +
392
+ `in ${duration}s`,
393
+ {
394
+ blockNumber: this.globalVariables.blockNumber,
395
+ successfulCount: result.length,
396
+ failedCount: failed.length,
397
+ duration,
398
+ rate,
399
+ totalPublicGas,
400
+ totalBlockGas,
401
+ totalSizeInBytes,
402
+ silentlySkippedCount,
403
+ silentlySkippedDurationMs,
404
+ },
405
+ );
406
+
407
+ return [result, failed, usedTxs, returns, debugLogs];
408
+ }
409
+
410
+ private async checkWorldStateUnchanged(
411
+ startStateReference: StateReference,
412
+ txHash: `0x${string}`,
413
+ cause: Error,
414
+ ): Promise<void> {
415
+ const endStateReference = await this.guardedMerkleTree.getUnderlyingFork().getStateReference();
416
+ if (!startStateReference.equals(endStateReference)) {
417
+ this.log.warn(`Fork state reference changed by tx ${txHash} after error in public processor`, {
418
+ expected: startStateReference.toInspect(),
419
+ actual: endStateReference.toInspect(),
420
+ cause,
421
+ });
422
+ throw new Error(`Fork state reference changed by tx ${txHash} after error in public processor`, { cause });
423
+ }
424
+ }
425
+
426
+ @trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.getTxHash().toString() }))
427
+ private async processTx(
428
+ tx: Tx,
429
+ deadline: Date | undefined,
430
+ signal: AbortSignal | undefined,
431
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
432
+ const [time, [processedTx, returnValues, debugLogs]] = await elapsed(() =>
433
+ this.processTxWithinDeadline(tx, deadline, signal),
434
+ );
435
+
436
+ this.log.verbose(
437
+ !tx.hasPublicCalls()
438
+ ? `Processed tx ${processedTx.hash} with no public calls in ${time}ms`
439
+ : `Processed tx ${processedTx.hash} with ${tx.numberOfPublicCalls()} public calls in ${time}ms`,
440
+ {
441
+ txHash: processedTx.hash,
442
+ txFee: processedTx.txEffect.transactionFee.toBigInt(),
443
+ revertCode: processedTx.txEffect.revertCode.getCode(),
444
+ revertReason: processedTx.revertReason,
445
+ gasUsed: processedTx.gasUsed,
446
+ publicDataWriteCount: processedTx.txEffect.publicDataWrites.length,
447
+ nullifierCount: processedTx.txEffect.nullifiers.length,
448
+ noteHashCount: processedTx.txEffect.noteHashes.length,
449
+ contractClassLogCount: processedTx.txEffect.contractClassLogs.length,
450
+ publicLogCount: processedTx.txEffect.publicLogs.length,
451
+ privateLogCount: processedTx.txEffect.privateLogs.length,
452
+ l2ToL1MessageCount: processedTx.txEffect.l2ToL1Msgs.length,
453
+ durationMs: time,
454
+ },
455
+ );
456
+
457
+ return [processedTx, returnValues ?? [], debugLogs];
458
+ }
459
+
460
+ private async doTreeInsertionsForPrivateOnlyTx(processedTx: ProcessedTx): Promise<void> {
461
+ const treeInsertionStart = process.hrtime.bigint();
462
+
463
+ // Update the state so that the next tx in the loop has the correct .startState
464
+ // NB: before this change, all .startStates were actually incorrect, but the issue was never caught because we either:
465
+ // a) had only 1 tx with public calls per block, so this loop had len 1
466
+ // b) always had a txHandler with the same db passed to it as this.db, which updated the db in buildBaseRollupHints in this loop
467
+ // To see how this ^ happens, move back to one shared db in test_context and run orchestrator_multi_public_functions.test.ts
468
+ // The below is taken from buildBaseRollupHints:
469
+ await this.guardedMerkleTree.appendLeaves(
470
+ MerkleTreeId.NOTE_HASH_TREE,
471
+ padArrayEnd(processedTx.txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
472
+ );
473
+ try {
474
+ await this.guardedMerkleTree.batchInsert(
475
+ MerkleTreeId.NULLIFIER_TREE,
476
+ padArrayEnd(processedTx.txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX).map(n => n.toBuffer()),
477
+ NULLIFIER_SUBTREE_HEIGHT,
478
+ );
479
+ } catch (cause) {
480
+ throw new Error(`Transaction ${processedTx.hash} failed with duplicate nullifiers`, { cause });
481
+ }
482
+
483
+ const treeInsertionEnd = process.hrtime.bigint();
484
+ this.metrics.recordTreeInsertions(Number(treeInsertionEnd - treeInsertionStart) / 1_000);
485
+ }
486
+
487
+ /** Processes the given tx within deadline or until the signal is aborted. */
488
+ private async processTxWithinDeadline(
489
+ tx: Tx,
490
+ deadline: Date | undefined,
491
+ signal: AbortSignal | undefined,
492
+ ): Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> {
493
+ const innerProcessFn: () => Promise<[ProcessedTx, NestedProcessReturnValues[] | undefined, DebugLog[]]> =
494
+ tx.hasPublicCalls() ? () => this.processTxWithPublicCalls(tx) : () => this.processPrivateOnlyTx(tx);
495
+
496
+ // Fake a delay per tx if instructed (used for tests)
497
+ const fakeDelayPerTxMs = this.opts.fakeProcessingDelayPerTxMs;
498
+ const processFn =
499
+ fakeDelayPerTxMs && fakeDelayPerTxMs > 0
500
+ ? async () => {
501
+ const result = await innerProcessFn();
502
+ this.log.warn(`Sleeping ${fakeDelayPerTxMs}ms after processing tx ${tx.getTxHash().toString()}`);
503
+ await sleep(fakeDelayPerTxMs);
504
+ return result;
505
+ }
506
+ : innerProcessFn;
507
+
508
+ const processingSignal = this.getProcessingSignal(tx, deadline, signal);
509
+ if (!processingSignal) {
510
+ return await processFn();
511
+ }
512
+
513
+ return await execWithSignal(
514
+ () => processFn(),
515
+ processingSignal,
516
+ signal =>
517
+ signal.reason?.name === 'TimeoutError' ? new PublicProcessorTimeoutError() : new PublicProcessorAbortError(),
518
+ );
519
+ }
520
+
521
+ private getProcessingSignal(tx: Tx, deadline: Date | undefined, signal: AbortSignal | undefined) {
522
+ if (!deadline) {
523
+ return signal;
524
+ }
525
+
526
+ const timeout = +deadline - this.dateProvider.now();
527
+ if (timeout <= 0) {
528
+ throw new PublicProcessorTimeoutError();
529
+ }
530
+
531
+ const txHash = tx.getTxHash();
532
+ this.log.debug(`Processing tx ${txHash.toString()} within ${timeout}ms`, {
533
+ deadline: deadline.toISOString(),
534
+ now: new Date(this.dateProvider.now()).toISOString(),
535
+ txHash,
536
+ });
537
+
538
+ const timeoutSignal = AbortSignal.timeout(timeout);
539
+ return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
540
+ }
541
+
542
+ /**
543
+ * Creates the public data write for paying the tx fee.
544
+ * This is used in private only txs, since for txs with public calls
545
+ * the avm handles the fee payment itself.
546
+ */
547
+ private async performFeePaymentPublicDataWrite(txFee: Fr, feePayer: AztecAddress): Promise<PublicDataWrite> {
548
+ const feeJuiceAddress = ProtocolContractAddress.FeeJuice;
549
+ const balanceSlot = await computeFeePayerBalanceStorageSlot(feePayer);
550
+ const leafSlot = await computeFeePayerBalanceLeafSlot(feePayer);
551
+ // This high-level db is used as a convenient helper. It could be done with the merkleTree directly.
552
+ const treesDB = new PublicTreesDB(this.guardedMerkleTree);
553
+
554
+ this.log.debug(`Deducting ${txFee.toBigInt()} balance in Fee Juice for ${feePayer}`);
555
+
556
+ const balance = await treesDB.storageRead(feeJuiceAddress, balanceSlot);
557
+
558
+ if (balance.lt(txFee)) {
559
+ throw new Error(
560
+ `Not enough balance for fee payer to pay for transaction (got ${balance.toBigInt()} needs ${txFee.toBigInt()})`,
561
+ );
562
+ }
563
+
564
+ const updatedBalance = balance.sub(txFee);
565
+ await treesDB.storageWrite(feeJuiceAddress, balanceSlot, updatedBalance);
566
+
567
+ return new PublicDataWrite(leafSlot, updatedBalance);
568
+ }
569
+
570
+ @trackSpan('PublicProcessor.processPrivateOnlyTx', (tx: Tx) => ({
571
+ [Attributes.TX_HASH]: tx.getTxHash().toString(),
572
+ }))
573
+ private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined, DebugLog[]]> {
574
+ const gasFees = this.globalVariables.gasFees;
575
+ const transactionFee = computeTransactionFee(gasFees, tx.data.constants.txContext.gasSettings, tx.data.gasUsed);
576
+
577
+ const feePaymentPublicDataWrite = await this.performFeePaymentPublicDataWrite(transactionFee, tx.data.feePayer);
578
+
579
+ const processedTx = makeProcessedTxFromPrivateOnlyTx(
580
+ tx,
581
+ transactionFee,
582
+ feePaymentPublicDataWrite,
583
+ this.globalVariables,
584
+ );
585
+
586
+ this.metrics.recordClassPublication(
587
+ ...tx
588
+ .getContractClassLogs()
589
+ .filter(log => ContractClassPublishedEvent.isContractClassPublishedEvent(log))
590
+ .map(log => ContractClassPublishedEvent.fromLog(log)),
591
+ );
592
+
593
+ // Fee payment insertion has already been done. Do the rest.
594
+ await this.doTreeInsertionsForPrivateOnlyTx(processedTx);
595
+
596
+ this.contractsDB.addNewContracts(tx);
597
+
598
+ return [processedTx, undefined, []];
599
+ }
600
+
601
+ @trackSpan('PublicProcessor.processTxWithPublicCalls', tx => ({
602
+ [Attributes.TX_HASH]: tx.getTxHash().toString(),
603
+ }))
604
+ private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[], DebugLog[]]> {
605
+ const timer = new Timer();
606
+
607
+ const result = await this.publicTxSimulator.simulate(tx);
608
+ // TODO: use the callStackMetadata here to extract more data about public execution
609
+ const { hints, publicInputs, publicTxEffect, gasUsed, revertCode /*callStackMetadata*/ } = result;
610
+
611
+ const contractClassLogs = revertCode.isOK()
612
+ ? tx.getContractClassLogs()
613
+ : tx.getSplitContractClassLogs(false /* revertible */);
614
+ this.metrics.recordClassPublication(
615
+ ...contractClassLogs
616
+ .filter(log => ContractClassPublishedEvent.isContractClassPublishedEvent(log))
617
+ .map(log => ContractClassPublishedEvent.fromLog(log)),
618
+ );
619
+
620
+ // TODO(fcarreiro): remove phase count metric.
621
+ const durationMs = timer.ms();
622
+ this.metrics.recordTx(/*phaseCount=*/ 1, durationMs, gasUsed.publicGas);
623
+
624
+ // Extract the return values from the call stack metadata.
625
+ const appLogicReturnValues: NestedProcessReturnValues[] = result.getAppLogicReturnValues();
626
+ // Extract the revert reason from the call stack metadata.
627
+ const revertReason = result.findRevertReason();
628
+ // Create proving request if we have hints and public inputs.
629
+ const avmProvingRequest =
630
+ hints && publicInputs ? PublicProcessor.generateProvingRequest(publicInputs, hints) : undefined;
631
+
632
+ const processedTx = makeProcessedTxFromTxWithPublicCalls(
633
+ tx,
634
+ this.globalVariables,
635
+ avmProvingRequest,
636
+ publicTxEffect,
637
+ gasUsed,
638
+ revertCode,
639
+ revertReason,
640
+ );
641
+
642
+ return [processedTx, appLogicReturnValues, result.logs ?? []];
643
+ }
644
+
645
+ /**
646
+ * Generate the proving request for the AVM circuit.
647
+ */
648
+ private static generateProvingRequest(
649
+ publicInputs: AvmCircuitPublicInputs,
650
+ hints: AvmExecutionHints = AvmExecutionHints.empty(),
651
+ ): AvmProvingRequest {
652
+ return {
653
+ type: ProvingRequestType.PUBLIC_VM,
654
+ inputs: new AvmCircuitInputs(hints, publicInputs),
655
+ };
656
+ }
657
+ }