Skip to content

Commit ad12d0f

Browse files
authored
refactor(txe): move orchestration logic out of rpc_translator (#23904)
1 parent 0790778 commit ad12d0f

2 files changed

Lines changed: 174 additions & 110 deletions

File tree

yarn-project/txe/src/rpc_translator.ts

Lines changed: 24 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -271,20 +271,8 @@ export class RPCTranslator {
271271
return callTxeHandler({
272272
oracle: 'aztec_txe_getPrivateEvents',
273273
inputs,
274-
handler: async ([selector, contractAddress, scope]) => {
275-
// TODO(F-335): Avoid doing the following 2 calls here.
276-
{
277-
await this.handlerAsTxe().syncContractNonOracleMethod(
278-
contractAddress,
279-
scope,
280-
this.stateHandler.getCurrentJob(),
281-
);
282-
// We cycle job to commit the stores after the contract sync.
283-
await this.stateHandler.cycleJob();
284-
}
285-
286-
return this.handlerAsTxe().getPrivateEvents(selector, contractAddress, scope);
287-
},
274+
handler: ([selector, contractAddress, scope]) =>
275+
this.stateHandler.getPrivateEvents(selector, contractAddress, scope),
288276
});
289277
}
290278

@@ -958,13 +946,12 @@ export class RPCTranslator {
958946
});
959947
}
960948

961-
// TODO(F-674): Move orchestration logic into the handler so the transport layer is pure serialize->delegate->deserialize.
962949
// eslint-disable-next-line camelcase
963950
aztec_txe_privateCallNewFlow(...inputs: ForeignCallArgs) {
964951
return callTxeHandler({
965952
oracle: 'aztec_txe_privateCallNewFlow',
966953
inputs,
967-
handler: async ([
954+
handler: ([
968955
from,
969956
targetContractAddress,
970957
functionSelector,
@@ -974,103 +961,43 @@ export class RPCTranslator {
974961
additionalScopes,
975962
authorizedUtilityCallTargets,
976963
gasSettings,
977-
]) => {
978-
const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
979-
const { returnValues, offchainEffects } = await this.handlerAsTxe().privateCallNewFlow(
980-
from?.value,
981-
targetContractAddress,
982-
functionSelector,
983-
args,
984-
argsHash,
985-
isStaticCall,
986-
additionalScopes,
987-
this.stateHandler.getCurrentJob(),
988-
authorizedUtilityCallTargets,
989-
gasSettings,
990-
);
991-
992-
// Private execution collects offchain effects inside PXE's PrivateExecutionOracle rather than
993-
// round-tripping them through `aztec_utl_emitOffchainEffect`, so the session buffer is empty
994-
// at this point. Drain the effects from the execution tree into the session buffer so the
995-
// next `env.offchain_messages()` call in the test sees them.
996-
for (const data of offchainEffects) {
997-
this.stateHandler.recordOffchainEffect(data);
998-
}
999-
1000-
// TODO(F-335): Avoid doing the following call here.
1001-
await this.stateHandler.cycleJob();
1002-
1003-
if (isStaticCall) {
1004-
// Static calls revert their checkpoint and mine no block, so there is no tx hash to tag
1005-
// offchain effects with. Querying `getLastTxEffects()` here would return an unrelated
1006-
// predecessor tx.
1007-
return { result: returnValues };
1008-
}
1009-
const { txHash } = await this.handlerAsTxe().getLastTxEffects();
1010-
return { result: returnValues, txHash: txHash.hash };
1011-
});
1012-
1013-
return returnValues;
1014-
},
964+
]) =>
965+
this.stateHandler.executePrivateCall(
966+
from,
967+
targetContractAddress,
968+
functionSelector,
969+
args,
970+
argsHash,
971+
isStaticCall,
972+
additionalScopes,
973+
authorizedUtilityCallTargets,
974+
gasSettings,
975+
),
1015976
});
1016977
}
1017978

1018-
// TODO(F-674): Move orchestration logic into the handler so the transport layer is pure serialize->delegate->deserialize.
1019979
// eslint-disable-next-line camelcase
1020980
aztec_txe_executeUtilityFunction(...inputs: ForeignCallArgs) {
1021981
return callTxeHandler({
1022982
oracle: 'aztec_txe_executeUtilityFunction',
1023983
inputs,
1024-
handler: async ([targetContractAddress, functionSelector, args, authorizedUtilityCallTargets]) => {
1025-
const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
1026-
const returnValues = await this.handlerAsTxe().executeUtilityFunction(
1027-
targetContractAddress,
1028-
functionSelector,
1029-
args,
1030-
this.stateHandler.getCurrentJob(),
1031-
authorizedUtilityCallTargets,
1032-
);
1033-
1034-
// TODO(F-335): Avoid doing the following call here.
1035-
await this.stateHandler.cycleJob();
1036-
1037-
return { result: returnValues };
1038-
});
1039-
1040-
return returnValues;
1041-
},
984+
handler: ([targetContractAddress, functionSelector, args, authorizedUtilityCallTargets]) =>
985+
this.stateHandler.executeUtilityFunction(
986+
targetContractAddress,
987+
functionSelector,
988+
args,
989+
authorizedUtilityCallTargets,
990+
),
1042991
});
1043992
}
1044993

1045-
// TODO(F-674): Move orchestration logic into the handler so the transport layer is pure serialize->delegate->deserialize.
1046994
// eslint-disable-next-line camelcase
1047995
aztec_txe_publicCallNewFlow(...inputs: ForeignCallArgs) {
1048996
return callTxeHandler({
1049997
oracle: 'aztec_txe_publicCallNewFlow',
1050998
inputs,
1051-
handler: async ([from, address, calldata, isStaticCall, gasSettings]) => {
1052-
const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
1053-
const returnValues = await this.handlerAsTxe().publicCallNewFlow(
1054-
from?.value,
1055-
address,
1056-
calldata,
1057-
isStaticCall,
1058-
gasSettings,
1059-
);
1060-
1061-
// TODO(F-335): Avoid doing the following call here.
1062-
await this.stateHandler.cycleJob();
1063-
1064-
if (isStaticCall) {
1065-
// See equivalent branch in `aztec_txe_privateCallNewFlow`.
1066-
return { result: returnValues };
1067-
}
1068-
const { txHash } = await this.handlerAsTxe().getLastTxEffects();
1069-
return { result: returnValues, txHash: txHash.hash };
1070-
});
1071-
1072-
return returnValues;
1073-
},
999+
handler: ([from, address, calldata, isStaticCall, gasSettings]) =>
1000+
this.stateHandler.executePublicCall(from, address, calldata, isStaticCall, gasSettings),
10741001
});
10751002
}
10761003

yarn-project/txe/src/txe_session.ts

Lines changed: 150 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
toACVMWitness,
3939
} from '@aztec/simulator/client';
4040
import { STANDARD_AUTH_REGISTRY_ADDRESS } from '@aztec/standard-contracts/auth-registry/constants';
41-
import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
41+
import { EventSelector, FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
4242
import type { AuthWitness } from '@aztec/stdlib/auth-witness';
4343
import { AztecAddress } from '@aztec/stdlib/aztec-address';
4444
import type { GasSettings } from '@aztec/stdlib/gas';
@@ -55,7 +55,7 @@ import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
5555
import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js';
5656
import { TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR } from './oracle/txe_oracle_version.js';
5757
import { TXEPrivateExecutionOracle } from './oracle/txe_private_execution_oracle.js';
58-
import { RPCTranslator } from './rpc_translator.js';
58+
import { RPCTranslator, UnavailableOracleError } from './rpc_translator.js';
5959
import { TXEArchiver } from './state_machine/archiver.js';
6060
import { TXEStateMachine } from './state_machine/index.js';
6161
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
@@ -127,14 +127,44 @@ export interface TXESessionStateHandler {
127127
): Promise<PrivateContextInputs>;
128128
enterUtilityState(contractAddress: Option<AztecAddress>): Promise<void>;
129129

130-
// TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
131-
cycleJob(): Promise<string>;
132-
getCurrentJob(): string;
130+
/**
131+
* Executes a top-level private call: runs the private function, drains its offchain effects into the session buffer,
132+
* commits the job, and (for non-static calls) tags the result with the mined tx hash.
133+
*/
134+
executePrivateCall(
135+
from: Option<AztecAddress>,
136+
targetContractAddress: AztecAddress,
137+
functionSelector: FunctionSelector,
138+
args: Fr[],
139+
argsHash: Fr,
140+
isStaticCall: boolean,
141+
additionalScopes: AztecAddress[],
142+
authorizedUtilityCallTargets: AztecAddress[],
143+
gasSettings: GasSettings,
144+
): Promise<Fr[]>;
145+
146+
/** Executes a top-level utility function and commits the job. */
147+
executeUtilityFunction(
148+
targetContractAddress: AztecAddress,
149+
functionSelector: FunctionSelector,
150+
args: Fr[],
151+
authorizedUtilityCallTargets: AztecAddress[],
152+
): Promise<Fr[]>;
133153

134154
/**
135-
* Runs an executor-style top-level call (private/public call, utility execution) with last-call tracking.
155+
* Executes a top-level public call, commits the job, and (for non-static calls) tags the result with the mined tx
156+
* hash.
136157
*/
137-
withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T>;
158+
executePublicCall(
159+
from: Option<AztecAddress>,
160+
targetContractAddress: AztecAddress,
161+
calldata: Fr[],
162+
isStaticCall: boolean,
163+
gasSettings: GasSettings,
164+
): Promise<Fr[]>;
165+
166+
/** Syncs the target contract and returns the private events it emitted matching the given selector and scope. */
167+
getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress): Promise<Fr[][]>;
138168

139169
/**
140170
* Captures a raw offchain effect payload for consumption from test environment. Called by the `emit_offchain_effect`
@@ -398,12 +428,8 @@ export class TXESession implements TXESessionStateHandler {
398428
}
399429
}
400430

401-
getCurrentJob(): string {
402-
return this.currentJobId;
403-
}
404-
405431
/** Commits the current job and begins a new one. Returns the new job ID. */
406-
async cycleJob(): Promise<string> {
432+
private async cycleJob(): Promise<string> {
407433
await this.jobCoordinator.commitJob(this.currentJobId);
408434
this.currentJobId = this.jobCoordinator.beginJob();
409435
return this.currentJobId;
@@ -433,7 +459,7 @@ export class TXESession implements TXESessionStateHandler {
433459
this.lastCallInfo.anchorBlockTimestamp = anchorBlockTimestamp;
434460
}
435461

436-
async withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T> {
462+
private async withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T> {
437463
this.resetLastCall();
438464
// Capture the anchor *before* `work` runs: private/public executor calls mine a new block as a
439465
// side effect, and that block's timestamp should not be attributed to this call's anchor.
@@ -463,6 +489,117 @@ export class TXESession implements TXESessionStateHandler {
463489
return { txHash, anchorBlockTimestamp };
464490
}
465491

492+
async executePrivateCall(
493+
from: Option<AztecAddress>,
494+
targetContractAddress: AztecAddress,
495+
functionSelector: FunctionSelector,
496+
args: Fr[],
497+
argsHash: Fr,
498+
isStaticCall: boolean,
499+
additionalScopes: AztecAddress[],
500+
authorizedUtilityCallTargets: AztecAddress[],
501+
gasSettings: GasSettings,
502+
): Promise<Fr[]> {
503+
const handler = this.handlerAsTxe();
504+
return await this.withTopLevelCallTracking(async () => {
505+
const { returnValues, offchainEffects } = await handler.privateCallNewFlow(
506+
from?.value,
507+
targetContractAddress,
508+
functionSelector,
509+
args,
510+
argsHash,
511+
isStaticCall,
512+
additionalScopes,
513+
this.currentJobId,
514+
authorizedUtilityCallTargets,
515+
gasSettings,
516+
);
517+
518+
// Private execution collects offchain effects inside PXE's PrivateExecutionOracle rather than round-tripping
519+
// them through `aztec_utl_emitOffchainEffect`, so the session buffer is empty at this point. Drain the effects
520+
// from the execution tree into the session buffer so the next `env.offchain_messages()` call in the test sees
521+
// them.
522+
for (const data of offchainEffects) {
523+
this.recordOffchainEffect(data);
524+
}
525+
526+
await this.cycleJob();
527+
528+
if (isStaticCall) {
529+
// Static calls revert their checkpoint and mine no block, so there is no tx hash to tag offchain effects
530+
// with. Querying `getLastTxEffects()` here would return an unrelated predecessor tx.
531+
return { result: returnValues };
532+
}
533+
const { txHash } = await handler.getLastTxEffects();
534+
return { result: returnValues, txHash: txHash.hash };
535+
});
536+
}
537+
538+
async executeUtilityFunction(
539+
targetContractAddress: AztecAddress,
540+
functionSelector: FunctionSelector,
541+
args: Fr[],
542+
authorizedUtilityCallTargets: AztecAddress[],
543+
): Promise<Fr[]> {
544+
const handler = this.handlerAsTxe();
545+
return await this.withTopLevelCallTracking(async () => {
546+
const returnValues = await handler.executeUtilityFunction(
547+
targetContractAddress,
548+
functionSelector,
549+
args,
550+
this.currentJobId,
551+
authorizedUtilityCallTargets,
552+
);
553+
554+
await this.cycleJob();
555+
556+
return { result: returnValues };
557+
});
558+
}
559+
560+
async executePublicCall(
561+
from: Option<AztecAddress>,
562+
targetContractAddress: AztecAddress,
563+
calldata: Fr[],
564+
isStaticCall: boolean,
565+
gasSettings: GasSettings,
566+
): Promise<Fr[]> {
567+
const handler = this.handlerAsTxe();
568+
return await this.withTopLevelCallTracking(async () => {
569+
const returnValues = await handler.publicCallNewFlow(
570+
from?.value,
571+
targetContractAddress,
572+
calldata,
573+
isStaticCall,
574+
gasSettings,
575+
);
576+
577+
await this.cycleJob();
578+
579+
if (isStaticCall) {
580+
// See the equivalent branch in `executePrivateCall`.
581+
return { result: returnValues };
582+
}
583+
const { txHash } = await handler.getLastTxEffects();
584+
return { result: returnValues, txHash: txHash.hash };
585+
});
586+
}
587+
588+
async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress): Promise<Fr[][]> {
589+
const handler = this.handlerAsTxe();
590+
await handler.syncContractNonOracleMethod(contractAddress, scope, this.currentJobId);
591+
// Cycle the job to commit the stores after the contract sync.
592+
await this.cycleJob();
593+
return handler.getPrivateEvents(selector, contractAddress, scope);
594+
}
595+
596+
private handlerAsTxe(): ITxeExecutionOracle {
597+
if (!('isTxe' in this.oracleHandler)) {
598+
throw new UnavailableOracleError('Txe');
599+
}
600+
return this.oracleHandler;
601+
}
602+
466603
setTxeOracleVersion(major: number, minor: number): void {
467604
if (major !== TXE_ORACLE_VERSION_MAJOR) {
468605
const hint =

0 commit comments

Comments
 (0)