@aztec/stdlib 3.0.0-nightly.20251205 → 3.0.0-nightly.20251207

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.
@@ -1,4 +1,5 @@
1
1
  import { RevertCode } from '../avm/revert_code.js';
2
+ import { SimulationError } from '../errors/simulation_error.js';
2
3
  import { Gas } from '../gas/gas.js';
3
4
  import { computeL2ToL1MessageHash } from '../hash/hash.js';
4
5
  import { TxEffect } from './tx_effect.js';
@@ -54,6 +55,8 @@ export function makeProcessedTxFromTxWithPublicCalls(tx, avmProvingRequest, gasU
54
55
  rollupVersion: globalVariables.version,
55
56
  chainId: globalVariables.chainId
56
57
  })), publicDataWrites, privateLogs, avmPublicInputs.accumulatedData.publicLogs.toLogs(), contractClassLogs);
58
+ // Some callers expect a revert reason if the tx reverted.
59
+ const finalRevertReason = revertReason === undefined && !revertCode.isOK() ? new SimulationError('TX reverted', /*functionErrorStack=*/ [], /*revertData=*/ []) : revertReason;
57
60
  return {
58
61
  hash: txEffect.txHash,
59
62
  data: tx.data,
@@ -63,6 +66,6 @@ export function makeProcessedTxFromTxWithPublicCalls(tx, avmProvingRequest, gasU
63
66
  txEffect,
64
67
  gasUsed,
65
68
  revertCode,
66
- revertReason
69
+ revertReason: finalRevertReason
67
70
  };
68
71
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/stdlib",
3
- "version": "3.0.0-nightly.20251205",
3
+ "version": "3.0.0-nightly.20251207",
4
4
  "type": "module",
5
5
  "inherits": [
6
6
  "../package.common.json",
@@ -72,13 +72,13 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@aws-sdk/client-s3": "^3.892.0",
75
- "@aztec/bb.js": "3.0.0-nightly.20251205",
76
- "@aztec/blob-lib": "3.0.0-nightly.20251205",
77
- "@aztec/constants": "3.0.0-nightly.20251205",
78
- "@aztec/ethereum": "3.0.0-nightly.20251205",
79
- "@aztec/foundation": "3.0.0-nightly.20251205",
80
- "@aztec/l1-artifacts": "3.0.0-nightly.20251205",
81
- "@aztec/noir-noirc_abi": "3.0.0-nightly.20251205",
75
+ "@aztec/bb.js": "3.0.0-nightly.20251207",
76
+ "@aztec/blob-lib": "3.0.0-nightly.20251207",
77
+ "@aztec/constants": "3.0.0-nightly.20251207",
78
+ "@aztec/ethereum": "3.0.0-nightly.20251207",
79
+ "@aztec/foundation": "3.0.0-nightly.20251207",
80
+ "@aztec/l1-artifacts": "3.0.0-nightly.20251207",
81
+ "@aztec/noir-noirc_abi": "3.0.0-nightly.20251207",
82
82
  "@google-cloud/storage": "^7.15.0",
83
83
  "axios": "^1.12.0",
84
84
  "json-stringify-deterministic": "1.0.12",
package/src/avm/avm.ts CHANGED
@@ -4,6 +4,7 @@ import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc';
4
4
 
5
5
  import { z } from 'zod';
6
6
 
7
+ import { FunctionSelector } from '../abi/index.js';
7
8
  import { AztecAddress } from '../aztec-address/index.js';
8
9
  import { AllContractDeploymentData, ContractDeploymentData } from '../contract/index.js';
9
10
  import { SimulationError } from '../errors/simulation_error.js';
@@ -28,6 +29,7 @@ import {
28
29
  TreeSnapshots,
29
30
  type Tx,
30
31
  } from '../tx/index.js';
32
+ import { TxExecutionPhase } from '../tx/processed_tx.js';
31
33
  import { WorldStateRevision } from '../world-state/world_state_revision.js';
32
34
  import { AvmCircuitPublicInputs } from './avm_circuit_public_inputs.js';
33
35
  import { serializeWithMessagePack } from './message_pack.js';
@@ -1042,17 +1044,146 @@ export class AvmCircuitInputs {
1042
1044
  }
1043
1045
  }
1044
1046
 
1047
+ // Metadata about a given (enqueued or external) call.
1048
+ export class CallStackMetadata {
1049
+ constructor(
1050
+ public phase: TxExecutionPhase,
1051
+ public contractAddress: Fr,
1052
+ public callerPc: number,
1053
+ public calldata: Fr[],
1054
+ public isStaticCall: boolean,
1055
+ public gasLimit: Gas,
1056
+ public output: Fr[], // returndata or revertdata.
1057
+ public internalCallStackAtExit: number[], // At return/revert time. Last one is exit PC.
1058
+ public haltingMessage: string | undefined,
1059
+ public reverted: boolean,
1060
+ public nested: CallStackMetadata[],
1061
+ public numNestedCalls: number, // This will be different from the size of the nested vector if we went past some limit.
1062
+ ) {}
1063
+
1064
+ static get schema(): ZodFor<CallStackMetadata> {
1065
+ return z
1066
+ .object({
1067
+ phase: z.nativeEnum(TxExecutionPhase),
1068
+ contractAddress: Fr.schema,
1069
+ callerPc: z.number(),
1070
+ calldata: Fr.schema.array(),
1071
+ isStaticCall: z.boolean(),
1072
+ gasLimit: Gas.schema,
1073
+ output: Fr.schema.array(),
1074
+ internalCallStackAtExit: z.number().array(),
1075
+ haltingMessage: NullishToUndefined(z.string()),
1076
+ reverted: z.boolean(),
1077
+ nested: CallStackMetadata.schema.array(),
1078
+ numNestedCalls: z.number(),
1079
+ })
1080
+ .transform(
1081
+ ({
1082
+ phase,
1083
+ contractAddress,
1084
+ callerPc,
1085
+ calldata,
1086
+ isStaticCall,
1087
+ gasLimit,
1088
+ output,
1089
+ internalCallStackAtExit,
1090
+ haltingMessage,
1091
+ reverted,
1092
+ nested,
1093
+ numNestedCalls,
1094
+ }) =>
1095
+ new CallStackMetadata(
1096
+ phase,
1097
+ contractAddress,
1098
+ callerPc,
1099
+ calldata,
1100
+ isStaticCall,
1101
+ gasLimit,
1102
+ output,
1103
+ internalCallStackAtExit,
1104
+ haltingMessage,
1105
+ reverted,
1106
+ nested,
1107
+ numNestedCalls,
1108
+ ),
1109
+ );
1110
+ }
1111
+
1112
+ /**
1113
+ * Creates a CallStackMetadata from a plain object without Zod validation.
1114
+ * This method is optimized for performance and skips validation, making it suitable
1115
+ * for deserializing trusted data (e.g., from C++ via MessagePack).
1116
+ * @param obj - Plain object containing CallStackMetadata fields
1117
+ * @returns A CallStackMetadata instance
1118
+ */
1119
+ static fromPlainObject(obj: any): CallStackMetadata {
1120
+ if (obj instanceof CallStackMetadata) {
1121
+ return obj;
1122
+ }
1123
+ return new CallStackMetadata(
1124
+ obj.phase,
1125
+ Fr.fromPlainObject(obj.contractAddress),
1126
+ obj.callerPc,
1127
+ obj.calldata.map((f: any) => Fr.fromPlainObject(f)),
1128
+ obj.isStaticCall,
1129
+ Gas.fromPlainObject(obj.gasLimit),
1130
+ obj.output.map((f: any) => Fr.fromPlainObject(f)),
1131
+ obj.internalCallStackAtExit.map((p: any) => Number(p)),
1132
+ obj.haltingMessage,
1133
+ obj.reverted,
1134
+ obj.nested.map((n: any) => CallStackMetadata.fromPlainObject(n)),
1135
+ obj.numNestedCalls,
1136
+ );
1137
+ }
1138
+
1139
+ public getRevertReason(): SimulationError | undefined {
1140
+ const failingCall = this.findDeepestRevert([this]);
1141
+
1142
+ if (!failingCall) {
1143
+ return undefined;
1144
+ }
1145
+
1146
+ const { stack, leaf } = failingCall;
1147
+ const aztecCallStack = stack.map(call => ({
1148
+ contractAddress: AztecAddress.fromField(call.contractAddress),
1149
+ functionSelector: call.calldata.length > 0 ? FunctionSelector.fromField(call.calldata[0]) : undefined,
1150
+ }));
1151
+
1152
+ // The Noir call stack is the internal call stack at exit of the failing call
1153
+ const noirCallStack = leaf.internalCallStackAtExit.map(pc => `0.${pc}`);
1154
+
1155
+ return new SimulationError(
1156
+ leaf.haltingMessage ?? 'Transaction reverted',
1157
+ aztecCallStack,
1158
+ leaf.output,
1159
+ noirCallStack,
1160
+ );
1161
+ }
1162
+
1163
+ private findDeepestRevert(
1164
+ calls: CallStackMetadata[],
1165
+ parentStack: CallStackMetadata[] = [],
1166
+ ): { stack: CallStackMetadata[]; leaf: CallStackMetadata } | undefined {
1167
+ for (const call of calls) {
1168
+ if (call.reverted) {
1169
+ const nested = this.findDeepestRevert(call.nested, [...parentStack, call]);
1170
+ return nested || { stack: [...parentStack, call], leaf: call };
1171
+ }
1172
+ }
1173
+ return undefined;
1174
+ }
1175
+ }
1176
+
1045
1177
  export class PublicTxResult {
1046
1178
  constructor(
1047
1179
  // Simulation result.
1048
1180
  public gasUsed: GasUsed,
1049
1181
  public revertCode: RevertCode,
1050
- public revertReason: SimulationError | undefined, // Revert reason, if any
1051
1182
  // These are only guaranteed to be present if the simulator is configured to collect them.
1052
- // NOTE: This list will be populated with one NestedProcessReturnValues per app logic enqueued call.
1053
- // IMPORTANT: The nesting will only be 1 level deep! You will get one result per enqueued call
1054
- // but no information about nested calls. This can be added later.
1055
- public appLogicReturnValues: NestedProcessReturnValues[], // One per enqueued call.
1183
+ // TODO(fcarreiro): Remove NestedProcessReturnValues[] once we migrate to the C++ simulator.
1184
+ public callStackMetadata:
1185
+ | CallStackMetadata[] // One per enqueued call. All phases.
1186
+ | NestedProcessReturnValues[], // One per enqueued call. App logic only.
1056
1187
  public logs: DebugLog[] | undefined,
1057
1188
  // For the proving request.
1058
1189
  public hints: AvmExecutionHints | undefined,
@@ -1068,8 +1199,7 @@ export class PublicTxResult {
1068
1199
  billedGas: Gas.empty(),
1069
1200
  },
1070
1201
  RevertCode.OK,
1071
- /*revertReason=*/ undefined,
1072
- /*appLogicReturnValues=*/ [],
1202
+ /*callStackMetadata=*/ [] as CallStackMetadata[],
1073
1203
  /*logs=*/ [],
1074
1204
  /*hints=*/ AvmExecutionHints.empty(),
1075
1205
  /*publicInputs=*/ AvmCircuitPublicInputs.empty(),
@@ -1082,37 +1212,122 @@ export class PublicTxResult {
1082
1212
  gasUsed: schemas.GasUsed,
1083
1213
  revertCode: RevertCode.schema,
1084
1214
  revertReason: NullishToUndefined(SimulationError.schema),
1085
- appLogicReturnValues: NestedProcessReturnValues.schema.array(),
1215
+ callStackMetadata: z.union([CallStackMetadata.schema.array(), NestedProcessReturnValues.schema.array()]),
1086
1216
  logs: NullishToUndefined(DebugLog.schema.array()),
1087
1217
  // For the proving request.
1088
1218
  publicInputs: AvmCircuitPublicInputs.schema,
1089
1219
  hints: NullishToUndefined(AvmExecutionHints.schema),
1090
1220
  })
1091
1221
  .transform(
1092
- ({ gasUsed, revertCode, revertReason, appLogicReturnValues, logs, hints, publicInputs }) =>
1093
- new PublicTxResult(
1094
- gasUsed,
1095
- revertCode as RevertCode,
1096
- revertReason,
1097
- appLogicReturnValues,
1098
- logs,
1099
- hints,
1100
- publicInputs,
1101
- ),
1222
+ ({ gasUsed, revertCode, callStackMetadata, logs, hints, publicInputs }) =>
1223
+ new PublicTxResult(gasUsed, revertCode as RevertCode, callStackMetadata, logs, hints, publicInputs),
1102
1224
  );
1103
1225
  }
1104
1226
 
1227
+ /**
1228
+ * Creates a PublicTxResult from a plain object without Zod validation.
1229
+ * This method is optimized for performance and skips validation, making it suitable
1230
+ * for deserializing trusted data (e.g., from C++ via MessagePack).
1231
+ * @param obj - Plain object containing PublicTxResult fields
1232
+ * @returns A PublicTxResult instance
1233
+ */
1105
1234
  static fromPlainObject(obj: any): PublicTxResult {
1106
1235
  return new PublicTxResult(
1107
1236
  GasUsed.fromPlainObject(obj.gasUsed),
1108
1237
  RevertCode.fromPlainObject(obj.revertCode),
1109
- /*revertReason=*/ undefined, // TODO(fcarreiro/mwood): add.
1110
- /*appLogicReturnValues=*/ obj.appLogicReturnValues.map(NestedProcessReturnValues.fromPlainObject),
1238
+ obj.callStackMetadata.map(CallStackMetadata.fromPlainObject), // Always CallStackMetadata[] from MessagePack.
1111
1239
  obj.logs?.map(DebugLog.fromPlainObject),
1112
1240
  obj.hints ? AvmExecutionHints.fromPlainObject(obj.hints) : undefined,
1113
1241
  AvmCircuitPublicInputs.fromPlainObject(obj.publicInputs),
1114
1242
  );
1115
1243
  }
1244
+
1245
+ /** Returns one level of return values for the app logic phase, one per enqueued call. */
1246
+ public getAppLogicReturnValues(): NestedProcessReturnValues[] {
1247
+ if (this.callStackMetadata.every(metadata => metadata instanceof CallStackMetadata)) {
1248
+ return this.callStackMetadata
1249
+ .filter(metadata => metadata.phase === TxExecutionPhase.APP_LOGIC)
1250
+ .map(metadata => new NestedProcessReturnValues(metadata.output));
1251
+ } else {
1252
+ return (this.callStackMetadata as NestedProcessReturnValues[]).map(
1253
+ metadata => new NestedProcessReturnValues(metadata.values, metadata.nested),
1254
+ );
1255
+ }
1256
+ }
1257
+
1258
+ public findRevertReason(): SimulationError | undefined {
1259
+ if (this.revertCode.isOK()) {
1260
+ return undefined;
1261
+ }
1262
+
1263
+ const callStackMetadata = this.callStackMetadata;
1264
+ // TODO(fcarreiro): Remove this after migration to the C++ simulator.
1265
+ // If the "stack" comes from TS, it will have this field.
1266
+ if ((callStackMetadata as any).revertReason !== undefined) {
1267
+ return (callStackMetadata as any).revertReason;
1268
+ }
1269
+
1270
+ // Handle CallStackMetadata[].
1271
+ let revertReason: SimulationError | undefined = undefined;
1272
+ for (const call of callStackMetadata) {
1273
+ revertReason = (call as CallStackMetadata).getRevertReason();
1274
+ if (revertReason) {
1275
+ break;
1276
+ }
1277
+ }
1278
+ return revertReason;
1279
+ }
1280
+ }
1281
+
1282
+ export class CollectionLimitsConfig {
1283
+ constructor(
1284
+ public readonly maxDebugLogMemoryReads: number,
1285
+ public readonly maxCalldataSizeInFields: number,
1286
+ public readonly maxReturndataSizeInFields: number,
1287
+ public readonly maxCallStackDepth: number,
1288
+ public readonly maxCallStackItems: number,
1289
+ ) {}
1290
+
1291
+ static from(obj: Partial<CollectionLimitsConfig>): CollectionLimitsConfig {
1292
+ return new CollectionLimitsConfig(
1293
+ obj.maxDebugLogMemoryReads ?? DEFAULT_MAX_DEBUG_LOG_MEMORY_READS,
1294
+ obj.maxCalldataSizeInFields ?? 300,
1295
+ obj.maxReturndataSizeInFields ?? 300,
1296
+ obj.maxCallStackDepth ?? 5,
1297
+ obj.maxCallStackItems ?? 100,
1298
+ );
1299
+ }
1300
+
1301
+ static empty() {
1302
+ return CollectionLimitsConfig.from({});
1303
+ }
1304
+
1305
+ static get schema() {
1306
+ return z
1307
+ .object({
1308
+ maxDebugLogMemoryReads: z.number(),
1309
+ maxCalldataSizeInFields: z.number(),
1310
+ maxReturndataSizeInFields: z.number(),
1311
+ maxCallStackDepth: z.number(),
1312
+ maxCallStackItems: z.number(),
1313
+ })
1314
+ .transform(
1315
+ ({
1316
+ maxDebugLogMemoryReads,
1317
+ maxCalldataSizeInFields,
1318
+ maxReturndataSizeInFields,
1319
+ maxCallStackDepth,
1320
+ maxCallStackItems,
1321
+ }) =>
1322
+ new CollectionLimitsConfig(
1323
+ maxDebugLogMemoryReads,
1324
+ maxCalldataSizeInFields,
1325
+ maxReturndataSizeInFields,
1326
+ maxCallStackDepth,
1327
+ maxCallStackItems,
1328
+ ),
1329
+ );
1330
+ }
1116
1331
  }
1117
1332
 
1118
1333
  export class PublicSimulatorConfig {
@@ -1122,8 +1337,8 @@ export class PublicSimulatorConfig {
1122
1337
  public readonly collectCallMetadata: boolean, // appLogicReturnValues.
1123
1338
  public readonly collectHints: boolean, // hints.
1124
1339
  public readonly collectDebugLogs: boolean, // logs.
1125
- public readonly maxDebugLogMemoryReads: number,
1126
1340
  public readonly collectStatistics: boolean, // timings etc.
1341
+ public readonly collectionLimits: CollectionLimitsConfig,
1127
1342
  ) {}
1128
1343
 
1129
1344
  static from(obj: Partial<PublicSimulatorConfig>): PublicSimulatorConfig {
@@ -1133,8 +1348,8 @@ export class PublicSimulatorConfig {
1133
1348
  obj.collectCallMetadata ?? false,
1134
1349
  obj.collectHints ?? false,
1135
1350
  obj.collectDebugLogs ?? false,
1136
- obj.maxDebugLogMemoryReads ?? DEFAULT_MAX_DEBUG_LOG_MEMORY_READS,
1137
1351
  obj.collectStatistics ?? false,
1352
+ obj.collectionLimits ?? CollectionLimitsConfig.empty(),
1138
1353
  );
1139
1354
  }
1140
1355
 
@@ -1150,8 +1365,8 @@ export class PublicSimulatorConfig {
1150
1365
  collectCallMetadata: z.boolean(),
1151
1366
  collectHints: z.boolean(),
1152
1367
  collectDebugLogs: z.boolean(),
1153
- maxDebugLogMemoryReads: z.number(),
1154
1368
  collectStatistics: z.boolean(),
1369
+ collectionLimits: CollectionLimitsConfig.schema,
1155
1370
  })
1156
1371
  .transform(PublicSimulatorConfig.from);
1157
1372
  }
@@ -103,6 +103,10 @@ export class L2BlockNew {
103
103
  };
104
104
  }
105
105
 
106
+ static empty() {
107
+ return new L2BlockNew(AppendOnlyTreeSnapshot.empty(), BlockHeader.empty(), Body.empty());
108
+ }
109
+
106
110
  /**
107
111
  * Creates an L2 block containing random data.
108
112
  * @param l2BlockNum - The number of the L2 block.
@@ -1,4 +1,10 @@
1
- import { BlockNumber, BlockNumberSchema, type EpochNumber, type SlotNumber } from '@aztec/foundation/branded-types';
1
+ import {
2
+ BlockNumber,
3
+ BlockNumberSchema,
4
+ type CheckpointNumber,
5
+ type EpochNumber,
6
+ type SlotNumber,
7
+ } from '@aztec/foundation/branded-types';
2
8
  import type { EthAddress } from '@aztec/foundation/eth-address';
3
9
  import type { Fr } from '@aztec/foundation/fields';
4
10
  import type { TypedEventEmitter } from '@aztec/foundation/types';
@@ -67,7 +73,15 @@ export interface L2BlockSource {
67
73
  */
68
74
  getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2Block[]>;
69
75
 
70
- getPublishedCheckpoints(from: number, limit: number): Promise<PublishedCheckpoint[]>;
76
+ getPublishedCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]>;
77
+
78
+ /**
79
+ * Gets a checkpoint by the archive root, which should be the root of the archive tree after the requested checkpoint
80
+ * is applied.
81
+ * @param archive - The new archive root of the checkpoint.
82
+ * @returns The requested checkpoint (or undefined if not found).
83
+ */
84
+ getCheckpointByArchive(archive: Fr): Promise<Checkpoint | undefined>;
71
85
 
72
86
  /** Equivalent to getBlocks but includes publish data. */
73
87
  getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<PublishedL2Block[]>;
@@ -74,7 +74,7 @@ export class Checkpoint {
74
74
  numBlocks = 1,
75
75
  startBlockNumber = 1,
76
76
  ...options
77
- }: { numBlocks?: number; startBlockNumber?: number } & Partial<FieldsOf<CheckpointHeader>> &
77
+ }: { numBlocks?: number; startBlockNumber?: number } & Partial<Parameters<typeof CheckpointHeader.random>[0]> &
78
78
  Partial<Parameters<typeof L2BlockNew.random>[1]> = {},
79
79
  ) {
80
80
  const header = CheckpointHeader.random(options);
@@ -88,8 +88,9 @@ export const ArchiverApiSchema: ApiSchemaFor<ArchiverApi> = {
88
88
  .returns(z.array(L2Block.schema)),
89
89
  getPublishedCheckpoints: z
90
90
  .function()
91
- .args(schemas.Integer, schemas.Integer)
91
+ .args(CheckpointNumberSchema, schemas.Integer)
92
92
  .returns(z.array(PublishedCheckpoint.schema)),
93
+ getCheckpointByArchive: z.function().args(schemas.Fr).returns(Checkpoint.schema.optional()),
93
94
  getPublishedBlocks: z
94
95
  .function()
95
96
  .args(BlockNumberSchema, schemas.Integer, optional(z.boolean()))
@@ -137,11 +137,13 @@ export class CheckpointHeader {
137
137
  });
138
138
  }
139
139
 
140
- static random(overrides: Partial<FieldsOf<CheckpointHeader>> = {}): CheckpointHeader {
140
+ static random(
141
+ overrides: Partial<FieldsOf<CheckpointHeader>> & Partial<FieldsOf<ContentCommitment>> = {},
142
+ ): CheckpointHeader {
141
143
  return CheckpointHeader.from({
142
144
  lastArchiveRoot: Fr.random(),
143
145
  blockHeadersHash: Fr.random(),
144
- contentCommitment: ContentCommitment.random(),
146
+ contentCommitment: ContentCommitment.random(overrides),
145
147
  slotNumber: SlotNumber(Math.floor(Math.random() * 1000) + 1),
146
148
  timestamp: BigInt(Math.floor(Date.now() / 1000)),
147
149
  coinbase: EthAddress.random(),
@@ -28,6 +28,10 @@ export class ContentCommitment {
28
28
  return [fields.blobsHash, fields.inHash, fields.outHash] as const;
29
29
  }
30
30
 
31
+ static from(fields: FieldsOf<ContentCommitment>) {
32
+ return new ContentCommitment(...ContentCommitment.getFields(fields));
33
+ }
34
+
31
35
  getSize() {
32
36
  return this.toBuffer().length;
33
37
  }
@@ -75,8 +79,13 @@ export class ContentCommitment {
75
79
  return new ContentCommitment(reader.readField(), reader.readField(), reader.readField());
76
80
  }
77
81
 
78
- static random(): ContentCommitment {
79
- return new ContentCommitment(Fr.random(), Fr.random(), Fr.random());
82
+ static random(overrides: Partial<FieldsOf<ContentCommitment>> = {}): ContentCommitment {
83
+ return ContentCommitment.from({
84
+ blobsHash: Fr.random(),
85
+ inHash: Fr.random(),
86
+ outHash: Fr.random(),
87
+ ...overrides,
88
+ });
80
89
  }
81
90
 
82
91
  static empty(): ContentCommitment {
@@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/fields';
3
3
  import type { AvmProvingRequest } from '../avm/avm_proving_request.js';
4
4
  import type { PublicDataWrite } from '../avm/public_data_write.js';
5
5
  import { RevertCode } from '../avm/revert_code.js';
6
- import type { SimulationError } from '../errors/simulation_error.js';
6
+ import { SimulationError } from '../errors/simulation_error.js';
7
7
  import { Gas } from '../gas/gas.js';
8
8
  import type { GasUsed } from '../gas/gas_used.js';
9
9
  import { computeL2ToL1MessageHash } from '../hash/hash.js';
@@ -173,6 +173,12 @@ export function makeProcessedTxFromTxWithPublicCalls(
173
173
  contractClassLogs,
174
174
  );
175
175
 
176
+ // Some callers expect a revert reason if the tx reverted.
177
+ const finalRevertReason =
178
+ revertReason === undefined && !revertCode.isOK()
179
+ ? new SimulationError('TX reverted', /*functionErrorStack=*/ [], /*revertData=*/ [])
180
+ : revertReason;
181
+
176
182
  return {
177
183
  hash: txEffect.txHash,
178
184
  data: tx.data,
@@ -182,6 +188,6 @@ export function makeProcessedTxFromTxWithPublicCalls(
182
188
  txEffect,
183
189
  gasUsed,
184
190
  revertCode,
185
- revertReason,
191
+ revertReason: finalRevertReason,
186
192
  };
187
193
  }