Skip to content

Commit

Permalink
chore(avm-simulator): track recursive public execution result in avm-…
Browse files Browse the repository at this point in the history
…simulator for integration with old kernel
  • Loading branch information
dbanks12 committed May 3, 2024
1 parent 03e1b93 commit b8db624
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 8 deletions.
111 changes: 106 additions & 5 deletions yarn-project/simulator/src/avm/journal/journal.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { UnencryptedL2Log } from '@aztec/circuit-types';
import { AztecAddress, EthAddress, L2ToL1Message } from '@aztec/circuits.js';
import {
AztecAddress,
ContractStorageRead,
ContractStorageUpdateRequest,
EthAddress,
L2ToL1Message,
NoteHash,
Nullifier,
ReadRequest,
SideEffect,
} from '@aztec/circuits.js';
import { EventSelector } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';

import { type PublicExecutionResult } from '../../index.js';
import { type HostStorage } from './host_storage.js';
import { Nullifiers } from './nullifiers.js';
import { PublicStorage } from './public_storage.js';
Expand Down Expand Up @@ -39,6 +50,23 @@ export type JournalData = {
currentStorageValue: Map<bigint, Map<bigint, Fr>>;
};

// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
type PartialPublicExecutionResult = {
nullifierReadRequests: ReadRequest[];
nullifierNonExistentReadRequests: ReadRequest[];
newNoteHashes: NoteHash[];
newL2ToL1Messages: L2ToL1Message[];
startSideEffectCounter: number;
newNullifiers: Nullifier[];
contractStorageReads: ContractStorageRead[];
contractStorageUpdateRequests: ContractStorageUpdateRequest[];
unencryptedLogsHashes: SideEffect[];
unencryptedLogs: UnencryptedL2Log[];
unencryptedLogPreimagesLength: Fr;
allUnencryptedLogs: UnencryptedL2Log[];
nestedExecutions: PublicExecutionResult[];
};

/**
* A class to manage persistable AVM state for contract calls.
* Maintains a cache of the current world state,
Expand Down Expand Up @@ -67,11 +95,30 @@ export class AvmPersistableStateManager {
public newL1Messages: L2ToL1Message[] = [];
public newLogs: UnencryptedL2Log[] = [];

// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
public transitionalExecutionResult: PartialPublicExecutionResult;

constructor(hostStorage: HostStorage, parent?: AvmPersistableStateManager) {
this.hostStorage = hostStorage;
this.publicStorage = new PublicStorage(hostStorage.publicStateDb, parent?.publicStorage);
this.nullifiers = new Nullifiers(hostStorage.commitmentsDb, parent?.nullifiers);
this.trace = new WorldStateAccessTrace(parent?.trace);

this.transitionalExecutionResult = {
nullifierReadRequests: [],
nullifierNonExistentReadRequests: [],
newNoteHashes: [],
newL2ToL1Messages: [],
startSideEffectCounter: this.trace.accessCounter,
newNullifiers: [],
contractStorageReads: [],
contractStorageUpdateRequests: [],
unencryptedLogsHashes: [],
unencryptedLogs: [],
unencryptedLogPreimagesLength: new Fr(0),
allUnencryptedLogs: [],
nestedExecutions: [],
};
}

/**
Expand All @@ -92,6 +139,12 @@ export class AvmPersistableStateManager {
this.log.debug(`storage(${storageAddress})@${slot} <- ${value}`);
// Cache storage writes for later reference/reads
this.publicStorage.write(storageAddress, slot, value);

// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
this.transitionalExecutionResult.contractStorageUpdateRequests.push(
new ContractStorageUpdateRequest(slot, value, this.trace.accessCounter, storageAddress),
);

// Trace all storage writes (even reverted ones)
this.trace.tracePublicStorageWrite(storageAddress, slot, value);
}
Expand All @@ -106,6 +159,12 @@ export class AvmPersistableStateManager {
public async readStorage(storageAddress: Fr, slot: Fr): Promise<Fr> {
const [exists, value] = await this.publicStorage.read(storageAddress, slot);
this.log.debug(`storage(${storageAddress})@${slot} ?? value: ${value}, exists: ${exists}.`);

// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
this.transitionalExecutionResult.contractStorageReads.push(
new ContractStorageRead(slot, value, this.trace.accessCounter, storageAddress),
);

// We want to keep track of all performed reads (even reverted ones)
this.trace.tracePublicStorageRead(storageAddress, slot, value, exists);
return Promise.resolve(value);
Expand Down Expand Up @@ -133,6 +192,9 @@ export class AvmPersistableStateManager {
* @param noteHash - the unsiloed note hash to write
*/
public writeNoteHash(storageAddress: Fr, noteHash: Fr) {
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
this.transitionalExecutionResult.newNoteHashes.push(new NoteHash(noteHash, this.trace.accessCounter));

this.log.debug(`noteHashes(${storageAddress}) += @${noteHash}.`);
this.trace.traceNewNoteHash(storageAddress, noteHash);
}
Expand All @@ -148,6 +210,16 @@ export class AvmPersistableStateManager {
this.log.debug(
`nullifiers(${storageAddress})@${nullifier} ?? leafIndex: ${leafIndex}, pending: ${isPending}, exists: ${exists}.`,
);

// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
if (exists) {
this.transitionalExecutionResult.nullifierReadRequests.push(new ReadRequest(nullifier, this.trace.accessCounter));
} else {
this.transitionalExecutionResult.nullifierNonExistentReadRequests.push(
new ReadRequest(nullifier, this.trace.accessCounter),
);
}

this.trace.traceNullifierCheck(storageAddress, nullifier, exists, isPending, leafIndex);
return Promise.resolve(exists);
}
Expand All @@ -158,6 +230,9 @@ export class AvmPersistableStateManager {
* @param nullifier - the unsiloed nullifier to write
*/
public async writeNullifier(storageAddress: Fr, nullifier: Fr) {
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
this.transitionalExecutionResult.newNullifiers.push(new Nullifier(nullifier, this.trace.accessCounter, Fr.ZERO));

this.log.debug(`nullifiers(${storageAddress}) += ${nullifier}.`);
// Cache pending nullifiers for later access
await this.nullifiers.append(storageAddress, nullifier);
Expand Down Expand Up @@ -189,18 +264,39 @@ export class AvmPersistableStateManager {
public writeL1Message(recipient: EthAddress | Fr, content: Fr) {
this.log.debug(`L1Messages(${recipient}) += ${content}.`);
const recipientAddress = recipient instanceof EthAddress ? recipient : EthAddress.fromField(recipient);
this.newL1Messages.push(new L2ToL1Message(recipientAddress, content));
const message = new L2ToL1Message(recipientAddress, content);
this.newL1Messages.push(message);

// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
this.transitionalExecutionResult.newL2ToL1Messages.push(message);
}

public writeLog(contractAddress: Fr, event: Fr, log: Fr[]) {
this.log.debug(`UnencryptedL2Log(${contractAddress}) += event ${event} with ${log.length} fields.`);
const L2log = new UnencryptedL2Log(
const ulog = new UnencryptedL2Log(
AztecAddress.fromField(contractAddress),
EventSelector.fromField(event),
Buffer.concat(log.map(f => f.toBuffer())),
);
this.newLogs.push(L2log);
this.trace.traceNewLog(Fr.fromBuffer(L2log.hash()));
const logHash = Fr.fromBuffer(ulog.hash());

// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
this.transitionalExecutionResult.unencryptedLogs.push(ulog);
this.transitionalExecutionResult.allUnencryptedLogs.push(ulog);
// this duplicates exactly what happens in the trace just for the purpose of transitional integration with the kernel
this.transitionalExecutionResult.unencryptedLogsHashes.push(
new SideEffect(logHash, new Fr(this.trace.accessCounter)),
);
// Duplicates computation performed in public_context.nr::emit_unencrypted_log
// 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4).
this.transitionalExecutionResult.unencryptedLogPreimagesLength = new Fr(
this.transitionalExecutionResult.unencryptedLogPreimagesLength.toNumber() + 44 + log.length * Fr.SIZE_IN_BYTES,
);
// TODO(6206): likely need to track this here and not just in the transitional logic.

// TODO(6205): why are logs pushed here but logs hashes are traced?
this.newLogs.push(ulog);
this.trace.traceNewLog(logHash);
}

/**
Expand All @@ -216,6 +312,11 @@ export class AvmPersistableStateManager {
// Accrued Substate
this.newL1Messages = this.newL1Messages.concat(nestedJournal.newL1Messages);
this.newLogs = this.newLogs.concat(nestedJournal.newLogs);

// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
this.transitionalExecutionResult.allUnencryptedLogs.concat(
nestedJournal.transitionalExecutionResult.allUnencryptedLogs,
);
}

/**
Expand Down
3 changes: 0 additions & 3 deletions yarn-project/simulator/src/avm/journal/trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,6 @@ export class WorldStateAccessTrace {
/**
* Merges another trace into this one
*
* - Public state journals (r/w logs), with the accessing being appended in chronological order
* - Utxo objects are concatenated
*
* @param incomingTrace - the incoming trace to merge into this instance
*/
public acceptAndMerge(incomingTrace: WorldStateAccessTrace) {
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/simulator/src/avm/opcodes/external_calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ abstract class ExternalCall extends Instruction {
);
const pxContext = createPublicExecutionContext(nestedContext, calldata);
const pxResults = await executePublicFunction(pxContext, /*nested=*/ true);
// store the old PublicExecutionResult object to maintain a recursive data structure for the old kernel
context.persistableState.transitionalExecutionResult.nestedExecutions.push(pxResults);
const nestedCallResults: AvmContractCallResults = convertPublicExecutionResult(pxResults);
updateAvmContextFromPublicExecutionResult(nestedContext, pxResults);
const nestedPersistableState = nestedContext.persistableState;
Expand Down

0 comments on commit b8db624

Please sign in to comment.