Skip to content

Commit 38595f7

Browse files
committed
feat: avm ts simulator accumulates debuglogs into ProcessedTx
1 parent 7b9f72c commit 38595f7

File tree

13 files changed

+142
-14
lines changed

13 files changed

+142
-14
lines changed

yarn-project/simulator/src/public/avm/opcodes/misc.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { jest } from '@jest/globals';
2+
import { mock } from 'jest-mock-extended';
23

4+
import type { PublicSideEffectTraceInterface } from '../../side_effect_trace_interface.js';
35
import { Field, Uint8, Uint32 } from '../avm_memory_types.js';
4-
import { initContext, initExecutionEnvironment } from '../fixtures/initializers.js';
6+
import { initContext, initExecutionEnvironment, initPersistableStateManager } from '../fixtures/initializers.js';
57
import { Opcode } from '../serialization/instruction_serialization.js';
68
import { DebugLog } from './misc.js';
79

@@ -30,7 +32,9 @@ describe('Misc Instructions', () => {
3032

3133
it('Should execute DebugLog in client-initiated simulation mode', async () => {
3234
const env = initExecutionEnvironment({ clientInitiatedSimulation: true });
33-
const context = initContext({ env });
35+
const trace = mock<PublicSideEffectTraceInterface>();
36+
const persistableState = initPersistableStateManager({ trace });
37+
const context = initContext({ env, persistableState });
3438
// Set up memory with message and fields
3539
const messageOffset = 10;
3640
const fieldsOffset = 100;
@@ -64,6 +68,10 @@ describe('Misc Instructions', () => {
6468

6569
// Check that logger.verbose was called with formatted message
6670
expect(mockVerbose).toHaveBeenCalledWith(`Hello ${fieldValue.toFr()}!`);
71+
72+
// expect debug log to be traced
73+
const msgId = 0; // TODO(dbanks12): implement messageId
74+
expect(trace.traceDebugLog).toHaveBeenCalledWith(msgId, [fieldValue.toFr()]);
6775
} finally {
6876
// Restore the mock
6977
mockIsVerbose.mockRestore();

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,19 @@ export class DebugLog extends Instruction {
5555
memory.checkTagsRange(TypeTag.UINT8, messageOffset, this.messageSize);
5656
memory.checkTagsRange(TypeTag.FIELD, fieldsOffset, fieldsSize);
5757

58-
// Interpret str<N> = [u8; N] to string.
59-
const messageAsStr = rawMessage.map(field => String.fromCharCode(field.toNumber())).join('');
60-
const formattedStr = applyStringFormatting(
61-
messageAsStr,
62-
fields.map(field => field.toFr()),
63-
);
58+
// Convert fields to Fr array for the side effect trace
59+
const fieldsAsFr = fields.map(field => field.toFr());
6460

65-
DebugLog.logger.verbose(formattedStr);
61+
const messageId = 0; // TODO(dbanks12): implement messageId as operand
62+
context.persistableState.writeDebugLog(messageId, fieldsAsFr);
63+
64+
if (DebugLog.logger.isLevelEnabled('verbose')) {
65+
// Interpret str<N> = [u8; N] to string.
66+
const messageAsStr = rawMessage.map(field => String.fromCharCode(field.toNumber())).join('');
67+
const formattedStr = applyStringFormatting(messageAsStr, fieldsAsFr);
68+
69+
DebugLog.logger.verbose(formattedStr);
70+
}
6671
}
6772
}
6873
}

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ export class PublicProcessor implements Traceable {
513513
private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
514514
const timer = new Timer();
515515

516-
const { avmProvingRequest, gasUsed, revertCode, revertReason, processedPhases } =
516+
const { avmProvingRequest, gasUsed, revertCode, revertReason, processedPhases, publicDebuggedLogs } =
517517
await this.publicTxSimulator.simulate(tx);
518518

519519
if (!avmProvingRequest) {
@@ -542,7 +542,14 @@ export class PublicProcessor implements Traceable {
542542
const durationMs = timer.ms();
543543
this.metrics.recordTx(phaseCount, durationMs, gasUsed.publicGas);
544544

545-
const processedTx = makeProcessedTxFromTxWithPublicCalls(tx, avmProvingRequest, gasUsed, revertCode, revertReason);
545+
const processedTx = makeProcessedTxFromTxWithPublicCalls(
546+
tx,
547+
avmProvingRequest,
548+
gasUsed,
549+
revertCode,
550+
revertReason,
551+
publicDebuggedLogs,
552+
);
546553

547554
const returnValues = processedPhases.find(({ phase }) => phase === TxExecutionPhase.APP_LOGIC)?.returnValues ?? [];
548555

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,13 @@ export class PublicTxContext {
289289
return txFee;
290290
}
291291

292+
/**
293+
* Get the public debugged logs accumulated during this transaction.
294+
*/
295+
public getDebugLogs() {
296+
return this.trace.getSideEffects().publicDebuggedLogs;
297+
}
298+
292299
/**
293300
* Generate the public inputs for the AVM circuit.
294301
*/

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from '@aztec/stdlib/avm';
1414
import { SimulationError } from '@aztec/stdlib/errors';
1515
import type { Gas, GasUsed } from '@aztec/stdlib/gas';
16+
import type { PublicDebuggedLog } from '@aztec/stdlib/logs';
1617
import { ProvingRequestType } from '@aztec/stdlib/proofs';
1718
import type { MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
1819
import {
@@ -47,6 +48,8 @@ export type PublicTxResult = {
4748
/** Revert reason, if any */
4849
revertReason?: SimulationError;
4950
processedPhases: ProcessedPhase[];
51+
/** Debug logs emitted during public execution */
52+
publicDebuggedLogs: PublicDebuggedLog[];
5053
};
5154

5255
export class PublicTxSimulator {
@@ -176,6 +179,7 @@ export class PublicTxSimulator {
176179
revertCode,
177180
revertReason: context.revertReason,
178181
processedPhases: processedPhases,
182+
publicDebuggedLogs: context.getDebugLogs(),
179183
};
180184
} finally {
181185
// Make sure there are no new contracts in the tx-level cache.

yarn-project/simulator/src/public/side_effect_trace.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { PublicDataUpdateRequest } from '@aztec/stdlib/avm';
1515
import { AztecAddress } from '@aztec/stdlib/aztec-address';
1616
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
1717
import { NoteHash, Nullifier } from '@aztec/stdlib/kernel';
18-
import { PublicLog } from '@aztec/stdlib/logs';
18+
import { PublicDebuggedLog, PublicLog } from '@aztec/stdlib/logs';
1919
import { L2ToL1Message } from '@aztec/stdlib/messaging';
2020
import { makeContractClassPublic } from '@aztec/stdlib/testing';
2121

@@ -31,6 +31,8 @@ describe('Public Side Effect Trace', () => {
3131
const recipient = Fr.random();
3232
const content = Fr.random();
3333
const log = [Fr.random(), Fr.random(), Fr.random()];
34+
const debugLogMessageId = 42;
35+
const debugLog = [new Fr(1), new Fr(2)];
3436

3537
let startCounter: number;
3638
let startCounterPlus1: number;
@@ -88,6 +90,11 @@ describe('Public Side Effect Trace', () => {
8890
expect(trace.getSideEffects().publicLogs).toEqual([expectedLog]);
8991
});
9092

93+
it('Should trace debug logs', () => {
94+
trace.traceDebugLog(debugLogMessageId, debugLog);
95+
expect(trace.getSideEffects().publicDebuggedLogs).toEqual([new PublicDebuggedLog(debugLogMessageId, debugLog)]);
96+
});
97+
9198
describe('Maximum accesses', () => {
9299
it('Should enforce maximum number of user public storage writes', async () => {
93100
for (let i = 0; i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; i++) {
@@ -228,6 +235,7 @@ describe('Public Side Effect Trace', () => {
228235
testCounter++;
229236
nestedTrace.tracePublicLog(address, log);
230237
testCounter++;
238+
nestedTrace.traceDebugLog(debugLogMessageId, debugLog); // no counter incr
231239

232240
trace.merge(nestedTrace, reverted);
233241

@@ -244,6 +252,8 @@ describe('Public Side Effect Trace', () => {
244252
expect(parentSideEffects.nullifiers).toEqual([]);
245253
expect(parentSideEffects.l2ToL1Msgs).toEqual([]);
246254
expect(parentSideEffects.publicLogs).toEqual([]);
255+
// debug logs don't get dropped on revert
256+
expect(parentSideEffects.publicDebuggedLogs).toEqual([new PublicDebuggedLog(debugLogMessageId, debugLog)]);
247257
// parent trace does not adopt nested call's writtenPublicDataSlots
248258
expect(trace.isStorageCold(address, slot)).toBe(true);
249259
} else {

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { PublicDataUpdateRequest } from '@aztec/stdlib/avm';
1616
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
1717
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
1818
import { NoteHash, Nullifier } from '@aztec/stdlib/kernel';
19-
import { PublicLog } from '@aztec/stdlib/logs';
19+
import { PublicDebuggedLog, PublicLog } from '@aztec/stdlib/logs';
2020
import { L2ToL1Message, ScopedL2ToL1Message } from '@aztec/stdlib/messaging';
2121

2222
import { strict as assert } from 'assert';
@@ -36,6 +36,7 @@ export type SideEffects = {
3636
nullifiers: Nullifier[];
3737
l2ToL1Msgs: ScopedL2ToL1Message[];
3838
publicLogs: PublicLog[];
39+
publicDebuggedLogs: PublicDebuggedLog[];
3940
};
4041

4142
export class SideEffectArrayLengths {
@@ -69,6 +70,7 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface {
6970
private nullifiers: Nullifier[] = [];
7071
private l2ToL1Messages: ScopedL2ToL1Message[] = [];
7172
private publicLogs: PublicLog[] = [];
73+
private publicDebuggedLogs: PublicDebuggedLog[] = [];
7274

7375
/** Make sure a forked trace is never merged twice. */
7476
private alreadyMergedIntoParent = false;
@@ -114,6 +116,9 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface {
114116
this.sideEffectCounter = forkedTrace.sideEffectCounter;
115117
this.uniqueClassIds.acceptAndMerge(forkedTrace.uniqueClassIds);
116118

119+
// Debug logs are always merged, even on revert
120+
this.publicDebuggedLogs.push(...forkedTrace.publicDebuggedLogs);
121+
117122
if (!reverted) {
118123
this.publicDataWrites.push(...forkedTrace.publicDataWrites);
119124
this.noteHashes.push(...forkedTrace.noteHashes);
@@ -237,6 +242,12 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface {
237242
this.incrementSideEffectCounter();
238243
}
239244

245+
public traceDebugLog(messageId: number, fields: Fr[]) {
246+
// Debug logs don't have a limit like other side effects
247+
const debugLog = new PublicDebuggedLog(messageId, fields);
248+
this.publicDebuggedLogs.push(debugLog);
249+
}
250+
240251
public traceGetContractClass(contractClassId: Fr, exists: boolean) {
241252
// We limit the number of unique contract class IDs due to hashing and the trace length limit.
242253
if (exists && !this.uniqueClassIds.has(contractClassId.toString())) {
@@ -260,6 +271,7 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface {
260271
nullifiers: this.nullifiers,
261272
l2ToL1Msgs: this.l2ToL1Messages,
262273
publicLogs: this.publicLogs,
274+
publicDebuggedLogs: this.publicDebuggedLogs,
263275
};
264276
}
265277
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ export interface PublicSideEffectTraceInterface {
1818
traceNewNullifier(siloedNullifier: Fr): void;
1919
traceNewL2ToL1Message(contractAddress: AztecAddress, recipient: Fr, content: Fr): void;
2020
tracePublicLog(contractAddress: AztecAddress, log: Fr[]): void;
21+
traceDebugLog(messageId: number, fields: Fr[]): void;
2122
traceGetContractClass(contractClassId: Fr, exists: boolean): void;
2223
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,15 @@ export class PublicPersistableStateManager {
329329
this.trace.tracePublicLog(contractAddress, log);
330330
}
331331

332+
/**
333+
* Write a debug log
334+
* @param messageId - message ID (currently always 0)
335+
* @param fields - debug log fields
336+
*/
337+
public writeDebugLog(messageId: number, fields: Fr[]) {
338+
this.trace.traceDebugLog(messageId, fields);
339+
}
340+
332341
/**
333342
* Get a contract instance.
334343
* @param contractAddress - address of the contract instance to retrieve.

yarn-project/stdlib/src/logs/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from './log_with_tx_data.js';
22
export * from './indexed_tagging_secret.js';
33
export * from './contract_class_log.js';
44
export * from './public_log.js';
5+
export * from './public_debugged_log.js';
56
export * from './private_log.js';
67
export * from './pending_tagged_log.js';
78
export * from './log_id.js';

0 commit comments

Comments
 (0)