@aztec/txe 0.0.1-commit.fff30aa → 0.0.1-dev
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.
- package/dest/oracle/interfaces.d.ts +10 -4
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.d.ts +12 -7
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +59 -33
- package/dest/rpc_translator.d.ts +20 -6
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +149 -21
- package/dest/state_machine/index.js +1 -1
- package/dest/txe_oracle_version.d.ts +10 -0
- package/dest/txe_oracle_version.d.ts.map +1 -0
- package/dest/txe_oracle_version.js +8 -0
- package/dest/txe_session.d.ts +61 -6
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +93 -21
- package/dest/util/encoding.d.ts +4 -1
- package/dest/util/encoding.d.ts.map +1 -1
- package/package.json +15 -15
- package/src/oracle/interfaces.ts +11 -3
- package/src/oracle/txe_oracle_top_level_context.ts +66 -42
- package/src/rpc_translator.ts +215 -31
- package/src/state_machine/index.ts +1 -1
- package/src/txe_oracle_version.ts +9 -0
- package/src/txe_session.ts +170 -17
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
|
|
3
|
-
MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT,
|
|
4
|
-
MAX_PROCESSABLE_L2_GAS,
|
|
5
|
-
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
6
|
-
} from '@aztec/constants';
|
|
1
|
+
import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
7
2
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
8
3
|
import { Schnorr } from '@aztec/foundation/crypto/schnorr';
|
|
9
4
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
@@ -15,13 +10,14 @@ import {
|
|
|
15
10
|
CapsuleService,
|
|
16
11
|
CapsuleStore,
|
|
17
12
|
type ContractStore,
|
|
18
|
-
type
|
|
13
|
+
type ExecutionHooks,
|
|
19
14
|
NoteStore,
|
|
20
15
|
ORACLE_VERSION_MAJOR,
|
|
21
16
|
PrivateEventStore,
|
|
22
17
|
RecipientTaggingStore,
|
|
23
18
|
SenderAddressBookStore,
|
|
24
19
|
SenderTaggingStore,
|
|
20
|
+
composeHooks,
|
|
25
21
|
enrichPublicSimulationError,
|
|
26
22
|
} from '@aztec/pxe/server';
|
|
27
23
|
import {
|
|
@@ -55,13 +51,7 @@ import { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
|
55
51
|
import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
56
52
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
57
53
|
import { type ContractInstanceWithAddress, computePartialAddress } from '@aztec/stdlib/contract';
|
|
58
|
-
import {
|
|
59
|
-
FALLBACK_TEARDOWN_DA_GAS_LIMIT,
|
|
60
|
-
FALLBACK_TEARDOWN_L2_GAS_LIMIT,
|
|
61
|
-
Gas,
|
|
62
|
-
GasFees,
|
|
63
|
-
GasSettings,
|
|
64
|
-
} from '@aztec/stdlib/gas';
|
|
54
|
+
import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
|
|
65
55
|
import { computeCalldataHash, computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
|
|
66
56
|
import {
|
|
67
57
|
PartialPrivateTailPublicInputsForPublic,
|
|
@@ -116,7 +106,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
116
106
|
private version: Fr,
|
|
117
107
|
private chainId: Fr,
|
|
118
108
|
private authwits: Map<string, AuthWitness>,
|
|
119
|
-
private readonly contractSyncService: ContractSyncService,
|
|
120
109
|
) {
|
|
121
110
|
this.logger = createLogger('txe:top_level_context');
|
|
122
111
|
this.logger.debug('Entering Top Level Context');
|
|
@@ -186,7 +175,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
186
175
|
|
|
187
176
|
const txEffects = block!.body.txEffects[0];
|
|
188
177
|
|
|
189
|
-
return {
|
|
178
|
+
return {
|
|
179
|
+
txHash: txEffects.txHash,
|
|
180
|
+
noteHashes: txEffects.noteHashes,
|
|
181
|
+
nullifiers: txEffects.nullifiers,
|
|
182
|
+
privateLogs: txEffects.privateLogs,
|
|
183
|
+
};
|
|
190
184
|
}
|
|
191
185
|
|
|
192
186
|
async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
|
|
@@ -315,13 +309,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
315
309
|
}
|
|
316
310
|
|
|
317
311
|
async privateCallNewFlow(
|
|
318
|
-
from: AztecAddress,
|
|
312
|
+
from: AztecAddress | undefined,
|
|
319
313
|
targetContractAddress: AztecAddress = AztecAddress.zero(),
|
|
320
314
|
functionSelector: FunctionSelector = FunctionSelector.empty(),
|
|
321
315
|
args: Fr[],
|
|
322
316
|
argsHash: Fr = Fr.zero(),
|
|
323
317
|
isStaticCall: boolean = false,
|
|
318
|
+
additionalScopes: AztecAddress[] = [],
|
|
324
319
|
jobId: string,
|
|
320
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
321
|
+
gasSettings: GasSettings,
|
|
325
322
|
) {
|
|
326
323
|
this.logger.verbose(
|
|
327
324
|
`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
@@ -335,9 +332,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
335
332
|
throw new Error(message);
|
|
336
333
|
}
|
|
337
334
|
|
|
338
|
-
|
|
339
|
-
// empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
|
|
340
|
-
const effectiveScopes = from.isZero() ? [] : [from];
|
|
335
|
+
const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
|
|
341
336
|
|
|
342
337
|
// Sync notes before executing private function to discover notes from previous transactions
|
|
343
338
|
const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
|
|
@@ -351,16 +346,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
351
346
|
utilityExecutor,
|
|
352
347
|
blockHeader,
|
|
353
348
|
jobId,
|
|
354
|
-
|
|
349
|
+
scopes,
|
|
355
350
|
);
|
|
356
351
|
|
|
357
352
|
const blockNumber = await this.getNextBlockNumber();
|
|
358
353
|
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
|
|
362
|
-
const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
|
|
363
|
-
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
354
|
+
const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
|
|
355
|
+
const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
|
|
364
356
|
|
|
365
357
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
366
358
|
|
|
@@ -394,19 +386,22 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
394
386
|
senderTaggingStore: this.senderTaggingStore,
|
|
395
387
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
396
388
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
397
|
-
capsuleService: new CapsuleService(this.capsuleStore,
|
|
389
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
398
390
|
privateEventStore: this.privateEventStore,
|
|
399
391
|
contractSyncService: this.stateMachine.contractSyncService,
|
|
400
392
|
jobId,
|
|
401
393
|
totalPublicCalldataCount: 0,
|
|
402
394
|
sideEffectCounter: minRevertibleSideEffectCounter,
|
|
403
|
-
scopes
|
|
395
|
+
scopes,
|
|
404
396
|
// In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
405
397
|
// contract would perform, including setting senderForTags.
|
|
406
398
|
senderForTags: from,
|
|
407
399
|
simulator,
|
|
408
400
|
messageContextService: this.stateMachine.messageContextService,
|
|
409
401
|
l2TipsStore: this.stateMachine.node,
|
|
402
|
+
hooks: composeHooks({
|
|
403
|
+
authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('private', authorizedUtilityCallTargets),
|
|
404
|
+
}),
|
|
410
405
|
});
|
|
411
406
|
|
|
412
407
|
// Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks.
|
|
@@ -514,11 +509,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
514
509
|
}
|
|
515
510
|
}
|
|
516
511
|
|
|
512
|
+
// Walk the nested private-call tree and collect every offchain effect the transaction emitted.
|
|
513
|
+
// PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
|
|
514
|
+
// `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
|
|
515
|
+
// we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
|
|
516
|
+
const offchainEffects = collectNested([executionResult], r => r.offchainEffects.map(e => e.data));
|
|
517
|
+
|
|
517
518
|
if (isStaticCall) {
|
|
518
519
|
await checkpoint!.revert();
|
|
519
520
|
|
|
520
521
|
await forkedWorldTrees.close();
|
|
521
|
-
return executionResult.returnValues ?? [];
|
|
522
|
+
return { returnValues: executionResult.returnValues ?? [], offchainEffects };
|
|
522
523
|
}
|
|
523
524
|
|
|
524
525
|
const txEffect = TxEffect.empty();
|
|
@@ -540,14 +541,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
540
541
|
|
|
541
542
|
await forkedWorldTrees.close();
|
|
542
543
|
|
|
543
|
-
return executionResult.returnValues ?? [];
|
|
544
|
+
return { returnValues: executionResult.returnValues ?? [], offchainEffects };
|
|
544
545
|
}
|
|
545
546
|
|
|
546
547
|
async publicCallNewFlow(
|
|
547
|
-
from: AztecAddress,
|
|
548
|
+
from: AztecAddress | undefined,
|
|
548
549
|
targetContractAddress: AztecAddress,
|
|
549
550
|
calldata: Fr[],
|
|
550
551
|
isStaticCall: boolean,
|
|
552
|
+
gasSettings: GasSettings,
|
|
551
553
|
) {
|
|
552
554
|
this.logger.verbose(
|
|
553
555
|
`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
@@ -555,12 +557,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
555
557
|
|
|
556
558
|
const blockNumber = await this.getNextBlockNumber();
|
|
557
559
|
|
|
558
|
-
const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
|
|
559
|
-
|
|
560
|
-
const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
|
|
561
|
-
|
|
562
|
-
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
563
|
-
|
|
564
560
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
565
561
|
|
|
566
562
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
@@ -612,7 +608,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
612
608
|
// may require producing reverts.
|
|
613
609
|
const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
614
610
|
revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
|
|
615
|
-
from,
|
|
611
|
+
from ?? AztecAddress.NULL_MSG_SENDER,
|
|
616
612
|
targetContractAddress,
|
|
617
613
|
isStaticCall,
|
|
618
614
|
calldataHash,
|
|
@@ -703,6 +699,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
703
699
|
functionSelector: FunctionSelector,
|
|
704
700
|
args: Fr[],
|
|
705
701
|
jobId: string,
|
|
702
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
706
703
|
) {
|
|
707
704
|
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
708
705
|
if (!artifact) {
|
|
@@ -733,10 +730,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
733
730
|
returnTypes: [],
|
|
734
731
|
});
|
|
735
732
|
|
|
736
|
-
return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId);
|
|
733
|
+
return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
|
|
737
734
|
}
|
|
738
735
|
|
|
739
|
-
private async executeUtilityCall(
|
|
736
|
+
private async executeUtilityCall(
|
|
737
|
+
call: FunctionCall,
|
|
738
|
+
scopes: AztecAddress[],
|
|
739
|
+
jobId: string,
|
|
740
|
+
authorizedUtilityCallTargets: AztecAddress[] = [],
|
|
741
|
+
): Promise<Fr[]> {
|
|
740
742
|
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
741
743
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
742
744
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
@@ -749,6 +751,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
749
751
|
|
|
750
752
|
try {
|
|
751
753
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
754
|
+
const simulator = new WASMSimulator();
|
|
755
|
+
const utilityExecutor = async (syncCall: FunctionCall, execScopes: AztecAddress[]) => {
|
|
756
|
+
await this.executeUtilityCall(syncCall, execScopes, jobId);
|
|
757
|
+
};
|
|
752
758
|
const oracle = new UtilityExecutionOracle({
|
|
753
759
|
contractAddress: call.to,
|
|
754
760
|
authWitnesses: [],
|
|
@@ -764,12 +770,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
764
770
|
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
765
771
|
privateEventStore: this.privateEventStore,
|
|
766
772
|
messageContextService: this.stateMachine.messageContextService,
|
|
767
|
-
contractSyncService: this.contractSyncService,
|
|
773
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
768
774
|
l2TipsStore: this.stateMachine.node,
|
|
769
775
|
jobId,
|
|
770
776
|
scopes,
|
|
777
|
+
simulator,
|
|
778
|
+
hooks: composeHooks({
|
|
779
|
+
authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets),
|
|
780
|
+
}),
|
|
781
|
+
utilityExecutor,
|
|
771
782
|
});
|
|
772
|
-
const acirExecutionResult = await
|
|
783
|
+
const acirExecutionResult = await simulator
|
|
773
784
|
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
774
785
|
.catch((err: Error) => {
|
|
775
786
|
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
@@ -800,4 +811,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
800
811
|
const header = await this.stateMachine.node.getBlockHeader('latest');
|
|
801
812
|
return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
|
|
802
813
|
}
|
|
814
|
+
|
|
815
|
+
private buildAuthorizeUtilityCallHook(
|
|
816
|
+
callerContext: 'private' | 'utility',
|
|
817
|
+
authorizedTargets: AztecAddress[],
|
|
818
|
+
): ExecutionHooks['authorizeUtilityCall'] | undefined {
|
|
819
|
+
if (authorizedTargets.length === 0) {
|
|
820
|
+
return undefined;
|
|
821
|
+
}
|
|
822
|
+
return req =>
|
|
823
|
+
Promise.resolve({
|
|
824
|
+
authorized: req.callerContext === callerContext && authorizedTargets.some(t => t.equals(req.target)),
|
|
825
|
+
});
|
|
826
|
+
}
|
|
803
827
|
}
|
package/src/rpc_translator.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { ContractInstanceWithAddress } from '@aztec/aztec.js/contracts';
|
|
2
2
|
import { Fr, Point } from '@aztec/aztec.js/fields';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ARCHIVE_HEIGHT,
|
|
5
|
+
MAX_NOTE_HASHES_PER_TX,
|
|
6
|
+
MAX_NULLIFIERS_PER_TX,
|
|
7
|
+
MAX_PRIVATE_LOGS_PER_TX,
|
|
8
|
+
PRIVATE_LOG_CIPHERTEXT_LEN,
|
|
9
|
+
PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
10
|
+
} from '@aztec/constants';
|
|
4
11
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
12
|
+
import { MembershipWitness } from '@aztec/foundation/trees';
|
|
5
13
|
import {
|
|
6
14
|
type IMiscOracle,
|
|
7
15
|
type IPrivateExecutionOracle,
|
|
@@ -11,8 +19,10 @@ import {
|
|
|
11
19
|
import { type ContractArtifact, EventSelector, FunctionSelector, NoteSelector } from '@aztec/stdlib/abi';
|
|
12
20
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
13
21
|
import { BlockHash } from '@aztec/stdlib/block';
|
|
22
|
+
import { GasSettings } from '@aztec/stdlib/gas';
|
|
14
23
|
|
|
15
24
|
import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
|
|
25
|
+
import { TXE_ORACLE_VERSION_MAJOR } from './txe_oracle_version.js';
|
|
16
26
|
import type { TXESessionStateHandler } from './txe_session.js';
|
|
17
27
|
import {
|
|
18
28
|
type ForeignCallArray,
|
|
@@ -102,6 +112,26 @@ export class RPCTranslator {
|
|
|
102
112
|
return this.oracleHandler;
|
|
103
113
|
}
|
|
104
114
|
|
|
115
|
+
// eslint-disable-next-line camelcase
|
|
116
|
+
aztec_txe_assertCompatibleOracleVersion(foreignMajor: ForeignCallSingle, foreignMinor: ForeignCallSingle) {
|
|
117
|
+
const major = fromSingle(foreignMajor).toNumber();
|
|
118
|
+
const minor = fromSingle(foreignMinor).toNumber();
|
|
119
|
+
|
|
120
|
+
if (major !== TXE_ORACLE_VERSION_MAJOR) {
|
|
121
|
+
const hint =
|
|
122
|
+
major > TXE_ORACLE_VERSION_MAJOR
|
|
123
|
+
? 'The test was compiled with a newer version of Aztec.nr than your test environment supports. Upgrade your test environment to a compatible version.'
|
|
124
|
+
: 'The test was compiled with an older version of Aztec.nr than your test environment supports. Recompile the test with a compatible version of Aztec.nr.';
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Incompatible test environment version: ${hint} See https://docs.aztec.network/errors/12 (expected test oracle major version ${TXE_ORACLE_VERSION_MAJOR}, got ${major})`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.stateHandler.setTxeOracleVersion({ major, minor });
|
|
131
|
+
|
|
132
|
+
return toForeignCallResult([]);
|
|
133
|
+
}
|
|
134
|
+
|
|
105
135
|
// TXE session state transition functions - these get handled by the state handler
|
|
106
136
|
|
|
107
137
|
// eslint-disable-next-line camelcase
|
|
@@ -117,6 +147,7 @@ export class RPCTranslator {
|
|
|
117
147
|
foreignContractAddressValue: ForeignCallSingle,
|
|
118
148
|
foreignAnchorBlockNumberIsSome: ForeignCallSingle,
|
|
119
149
|
foreignAnchorBlockNumberValue: ForeignCallSingle,
|
|
150
|
+
foreignGasSettings: ForeignCallArray,
|
|
120
151
|
) {
|
|
121
152
|
const contractAddress = fromSingle(foreignContractAddressIsSome).toBool()
|
|
122
153
|
? AztecAddress.fromField(fromSingle(foreignContractAddressValue))
|
|
@@ -126,7 +157,13 @@ export class RPCTranslator {
|
|
|
126
157
|
? BlockNumber(fromSingle(foreignAnchorBlockNumberValue).toNumber())
|
|
127
158
|
: undefined;
|
|
128
159
|
|
|
129
|
-
const
|
|
160
|
+
const gasSettings = GasSettings.fromFields(fromArray(foreignGasSettings));
|
|
161
|
+
|
|
162
|
+
const privateContextInputs = await this.stateHandler.enterPrivateState(
|
|
163
|
+
contractAddress,
|
|
164
|
+
anchorBlockNumber,
|
|
165
|
+
gasSettings,
|
|
166
|
+
);
|
|
130
167
|
|
|
131
168
|
return toForeignCallResult(privateContextInputs.toFields().map(toSingle));
|
|
132
169
|
}
|
|
@@ -289,15 +326,81 @@ export class RPCTranslator {
|
|
|
289
326
|
|
|
290
327
|
// eslint-disable-next-line camelcase
|
|
291
328
|
async aztec_txe_getLastTxEffects() {
|
|
292
|
-
const { txHash, noteHashes, nullifiers } = await this.handlerAsTxe().getLastTxEffects();
|
|
329
|
+
const { txHash, noteHashes, nullifiers, privateLogs } = await this.handlerAsTxe().getLastTxEffects();
|
|
330
|
+
|
|
331
|
+
if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
|
|
332
|
+
throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Same workaround as `aztec_txe_getPrivateEvents`: Noir cannot yet return nested structs with arrays, so we return
|
|
336
|
+
// a flat multidimensional array plus per-log lengths and the total count, and reassemble into a
|
|
337
|
+
// `BoundedVec<BoundedVec<T>>` on the Noir side. Each log contributes only its emitted fields. The rest
|
|
338
|
+
// is zero-padded to `PRIVATE_LOG_SIZE_IN_FIELDS`.
|
|
339
|
+
const emittedLogs = privateLogs.map(log => log.getEmittedFields());
|
|
340
|
+
|
|
341
|
+
const rawLogStorage = emittedLogs
|
|
342
|
+
.map(fields => fields.concat(Array(PRIVATE_LOG_SIZE_IN_FIELDS - fields.length).fill(new Fr(0))))
|
|
343
|
+
.concat(
|
|
344
|
+
Array(MAX_PRIVATE_LOGS_PER_TX - emittedLogs.length).fill(Array(PRIVATE_LOG_SIZE_IN_FIELDS).fill(new Fr(0))),
|
|
345
|
+
)
|
|
346
|
+
.flat();
|
|
347
|
+
|
|
348
|
+
const logLengths = emittedLogs
|
|
349
|
+
.map(fields => new Fr(fields.length))
|
|
350
|
+
.concat(Array(MAX_PRIVATE_LOGS_PER_TX - emittedLogs.length).fill(new Fr(0)));
|
|
351
|
+
|
|
352
|
+
const logCount = new Fr(emittedLogs.length);
|
|
293
353
|
|
|
294
354
|
return toForeignCallResult([
|
|
295
355
|
toSingle(txHash.hash),
|
|
296
356
|
...arrayToBoundedVec(toArray(noteHashes), MAX_NOTE_HASHES_PER_TX),
|
|
297
357
|
...arrayToBoundedVec(toArray(nullifiers), MAX_NULLIFIERS_PER_TX),
|
|
358
|
+
toArray(rawLogStorage),
|
|
359
|
+
toArray(logLengths),
|
|
360
|
+
toSingle(logCount),
|
|
298
361
|
]);
|
|
299
362
|
}
|
|
300
363
|
|
|
364
|
+
// eslint-disable-next-line camelcase
|
|
365
|
+
aztec_txe_getLastCallOffchainEffects() {
|
|
366
|
+
// This oracle returns all offchain effect payloads (messages, authwit requests, etc.) emitted by the last top-level call,
|
|
367
|
+
// MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY is arbitrarily set at 64 because we need a bound. Nothing inherent about it.
|
|
368
|
+
const MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY = 64;
|
|
369
|
+
// Must match MAX_OFFCHAIN_EFFECT_LEN in txe_oracles.nr.
|
|
370
|
+
const MAX_OFFCHAIN_EFFECT_LEN = 2 + PRIVATE_LOG_CIPHERTEXT_LEN;
|
|
371
|
+
|
|
372
|
+
const { effects } = this.stateHandler.getLastCallOffchainEffects();
|
|
373
|
+
|
|
374
|
+
if (effects.length > MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY) {
|
|
375
|
+
throw new Error(`${effects.length} offchain effects exceed max ${MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY}`);
|
|
376
|
+
}
|
|
377
|
+
if (effects.some(e => e.length > MAX_OFFCHAIN_EFFECT_LEN)) {
|
|
378
|
+
throw new Error(`Some offchain effect has length larger than max ${MAX_OFFCHAIN_EFFECT_LEN}`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const rawArrayStorage = effects
|
|
382
|
+
.map(e => e.concat(Array(MAX_OFFCHAIN_EFFECT_LEN - e.length).fill(new Fr(0))))
|
|
383
|
+
.concat(
|
|
384
|
+
Array(MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY - effects.length).fill(Array(MAX_OFFCHAIN_EFFECT_LEN).fill(new Fr(0))),
|
|
385
|
+
)
|
|
386
|
+
.flat();
|
|
387
|
+
|
|
388
|
+
const effectLengths = effects
|
|
389
|
+
.map(e => new Fr(e.length))
|
|
390
|
+
.concat(Array(MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY - effects.length).fill(new Fr(0)));
|
|
391
|
+
|
|
392
|
+
const count = new Fr(effects.length);
|
|
393
|
+
|
|
394
|
+
return toForeignCallResult([toArray(rawArrayStorage), toArray(effectLengths), toSingle(count)]);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// eslint-disable-next-line camelcase
|
|
398
|
+
aztec_txe_getLastCallContext() {
|
|
399
|
+
const { txHash, anchorBlockTimestamp } = this.stateHandler.getLastCallContext();
|
|
400
|
+
const isSome = txHash.isZero() ? 0 : 1;
|
|
401
|
+
return toForeignCallResult([toSingle(isSome), toSingle(txHash), toSingle(new Fr(anchorBlockTimestamp))]);
|
|
402
|
+
}
|
|
403
|
+
|
|
301
404
|
// eslint-disable-next-line camelcase
|
|
302
405
|
async aztec_txe_getPrivateEvents(
|
|
303
406
|
foreignSelector: ForeignCallSingle,
|
|
@@ -704,6 +807,7 @@ export class RPCTranslator {
|
|
|
704
807
|
return toForeignCallResult(witness.toNoirRepresentation());
|
|
705
808
|
}
|
|
706
809
|
|
|
810
|
+
// TODO(https://linear.app/aztec-labs/issue/F-651): drop this
|
|
707
811
|
// eslint-disable-next-line camelcase
|
|
708
812
|
async aztec_utl_getBlockHashMembershipWitness(
|
|
709
813
|
foreignAnchorBlockHash: ForeignCallSingle,
|
|
@@ -722,6 +826,20 @@ export class RPCTranslator {
|
|
|
722
826
|
return toForeignCallResult(witness.toNoirRepresentation());
|
|
723
827
|
}
|
|
724
828
|
|
|
829
|
+
// TODO(https://linear.app/aztec-labs/issue/F-651): rename to aztec_utl_getBlockHashMembershipWitness
|
|
830
|
+
// eslint-disable-next-line camelcase
|
|
831
|
+
async aztec_utl_getBlockHashMembershipWitnessV2(
|
|
832
|
+
foreignAnchorBlockHash: ForeignCallSingle,
|
|
833
|
+
foreignBlockHash: ForeignCallSingle,
|
|
834
|
+
) {
|
|
835
|
+
const anchorBlockHash = new BlockHash(fromSingle(foreignAnchorBlockHash));
|
|
836
|
+
const blockHash = new BlockHash(fromSingle(foreignBlockHash));
|
|
837
|
+
|
|
838
|
+
const witness = await this.handlerAsUtility().getBlockHashMembershipWitness(anchorBlockHash, blockHash);
|
|
839
|
+
const effective = witness ?? MembershipWitness.empty(ARCHIVE_HEIGHT);
|
|
840
|
+
return toForeignCallResult([toSingle(new Fr(witness !== undefined ? 1 : 0)), ...effective.toNoirRepresentation()]);
|
|
841
|
+
}
|
|
842
|
+
|
|
725
843
|
// eslint-disable-next-line camelcase
|
|
726
844
|
async aztec_utl_getLowNullifierMembershipWitness(
|
|
727
845
|
foreignBlockHash: ForeignCallSingle,
|
|
@@ -1071,8 +1189,13 @@ export class RPCTranslator {
|
|
|
1071
1189
|
}
|
|
1072
1190
|
|
|
1073
1191
|
// eslint-disable-next-line camelcase
|
|
1074
|
-
aztec_utl_emitOffchainEffect(
|
|
1075
|
-
|
|
1192
|
+
aztec_utl_emitOffchainEffect(foreignData: ForeignCallArray) {
|
|
1193
|
+
// Record the raw payload against the currently-executing top-level call. The Noir side
|
|
1194
|
+
// (via `env.offchain_messages()`) is responsible for decoding the protocol-reserved prefix
|
|
1195
|
+
// (`OFFCHAIN_MESSAGE_IDENTIFIER`, recipient) and turning each payload into an `OffchainMessage` struct suitable
|
|
1196
|
+
// for `offchain_receive`.
|
|
1197
|
+
this.stateHandler.recordOffchainEffect(fromArray(foreignData));
|
|
1198
|
+
return Promise.resolve(toForeignCallResult([]));
|
|
1076
1199
|
}
|
|
1077
1200
|
|
|
1078
1201
|
// AVM opcodes
|
|
@@ -1267,32 +1390,64 @@ export class RPCTranslator {
|
|
|
1267
1390
|
|
|
1268
1391
|
// eslint-disable-next-line camelcase
|
|
1269
1392
|
async aztec_txe_privateCallNewFlow(
|
|
1270
|
-
|
|
1393
|
+
foreignFromIsSome: ForeignCallSingle,
|
|
1394
|
+
foreignFromValue: ForeignCallSingle,
|
|
1271
1395
|
foreignTargetContractAddress: ForeignCallSingle,
|
|
1272
1396
|
foreignFunctionSelector: ForeignCallSingle,
|
|
1273
1397
|
foreignArgs: ForeignCallArray,
|
|
1274
1398
|
foreignArgsHash: ForeignCallSingle,
|
|
1275
1399
|
foreignIsStaticCall: ForeignCallSingle,
|
|
1400
|
+
foreignAdditionalScopes: ForeignCallArray,
|
|
1401
|
+
foreignAuthorizedUtilityCallTargets: ForeignCallArray,
|
|
1402
|
+
foreignGasSettings: ForeignCallArray,
|
|
1276
1403
|
) {
|
|
1277
|
-
const from = addressFromSingle(
|
|
1404
|
+
const from = fromSingle(foreignFromIsSome).toBool() ? addressFromSingle(foreignFromValue) : undefined;
|
|
1278
1405
|
const targetContractAddress = addressFromSingle(foreignTargetContractAddress);
|
|
1279
1406
|
const functionSelector = FunctionSelector.fromField(fromSingle(foreignFunctionSelector));
|
|
1280
1407
|
const args = fromArray(foreignArgs);
|
|
1281
1408
|
const argsHash = fromSingle(foreignArgsHash);
|
|
1282
1409
|
const isStaticCall = fromSingle(foreignIsStaticCall).toBool();
|
|
1283
|
-
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
targetContractAddress,
|
|
1287
|
-
functionSelector,
|
|
1288
|
-
args,
|
|
1289
|
-
argsHash,
|
|
1290
|
-
isStaticCall,
|
|
1291
|
-
this.stateHandler.getCurrentJob(),
|
|
1410
|
+
const additionalScopes = fromArray(foreignAdditionalScopes).map(field => AztecAddress.fromField(field));
|
|
1411
|
+
const authorizedUtilityCallTargets = fromArray(foreignAuthorizedUtilityCallTargets).map(field =>
|
|
1412
|
+
AztecAddress.fromField(field),
|
|
1292
1413
|
);
|
|
1414
|
+
const gasSettings = GasSettings.fromFields(fromArray(foreignGasSettings));
|
|
1415
|
+
|
|
1416
|
+
const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
|
|
1417
|
+
const { returnValues, offchainEffects } = await this.handlerAsTxe().privateCallNewFlow(
|
|
1418
|
+
from,
|
|
1419
|
+
targetContractAddress,
|
|
1420
|
+
functionSelector,
|
|
1421
|
+
args,
|
|
1422
|
+
argsHash,
|
|
1423
|
+
isStaticCall,
|
|
1424
|
+
additionalScopes,
|
|
1425
|
+
this.stateHandler.getCurrentJob(),
|
|
1426
|
+
authorizedUtilityCallTargets,
|
|
1427
|
+
gasSettings,
|
|
1428
|
+
);
|
|
1429
|
+
|
|
1430
|
+
// Private execution collects offchain effects inside PXE's PrivateExecutionOracle rather than
|
|
1431
|
+
// round-tripping them through `aztec_utl_emitOffchainEffect`, so the session buffer is empty
|
|
1432
|
+
// at this point. Drain the effects from the execution tree into the session buffer so the
|
|
1433
|
+
// next `env.offchain_messages()` call in the test sees them.
|
|
1434
|
+
for (const data of offchainEffects) {
|
|
1435
|
+
this.stateHandler.recordOffchainEffect(data);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
// TODO(F-335): Avoid doing the following call here.
|
|
1439
|
+
await this.stateHandler.cycleJob();
|
|
1440
|
+
|
|
1441
|
+
if (isStaticCall) {
|
|
1442
|
+
// Static calls revert their checkpoint and mine no block, so there is no tx hash to tag
|
|
1443
|
+
// offchain effects with. Querying `getLastTxEffects()` here would return an unrelated
|
|
1444
|
+
// predecessor tx.
|
|
1445
|
+
return { result: returnValues };
|
|
1446
|
+
}
|
|
1447
|
+
const { txHash } = await this.handlerAsTxe().getLastTxEffects();
|
|
1448
|
+
return { result: returnValues, txHash: txHash.hash };
|
|
1449
|
+
});
|
|
1293
1450
|
|
|
1294
|
-
// TODO(F-335): Avoid doing the following call here.
|
|
1295
|
-
await this.stateHandler.cycleJob();
|
|
1296
1451
|
return toForeignCallResult([toArray(returnValues)]);
|
|
1297
1452
|
}
|
|
1298
1453
|
|
|
@@ -1301,39 +1456,68 @@ export class RPCTranslator {
|
|
|
1301
1456
|
foreignTargetContractAddress: ForeignCallSingle,
|
|
1302
1457
|
foreignFunctionSelector: ForeignCallSingle,
|
|
1303
1458
|
foreignArgs: ForeignCallArray,
|
|
1459
|
+
foreignAuthorizedUtilityCallTargets: ForeignCallArray,
|
|
1304
1460
|
) {
|
|
1305
1461
|
const targetContractAddress = addressFromSingle(foreignTargetContractAddress);
|
|
1306
1462
|
const functionSelector = FunctionSelector.fromField(fromSingle(foreignFunctionSelector));
|
|
1307
1463
|
const args = fromArray(foreignArgs);
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
targetContractAddress,
|
|
1311
|
-
functionSelector,
|
|
1312
|
-
args,
|
|
1313
|
-
this.stateHandler.getCurrentJob(),
|
|
1464
|
+
const authorizedUtilityCallTargets = fromArray(foreignAuthorizedUtilityCallTargets).map(field =>
|
|
1465
|
+
AztecAddress.fromField(field),
|
|
1314
1466
|
);
|
|
1315
1467
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1468
|
+
const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
|
|
1469
|
+
const returnValues = await this.handlerAsTxe().executeUtilityFunction(
|
|
1470
|
+
targetContractAddress,
|
|
1471
|
+
functionSelector,
|
|
1472
|
+
args,
|
|
1473
|
+
this.stateHandler.getCurrentJob(),
|
|
1474
|
+
authorizedUtilityCallTargets,
|
|
1475
|
+
);
|
|
1476
|
+
|
|
1477
|
+
// TODO(F-335): Avoid doing the following call here.
|
|
1478
|
+
await this.stateHandler.cycleJob();
|
|
1479
|
+
|
|
1480
|
+
return { result: returnValues };
|
|
1481
|
+
});
|
|
1482
|
+
|
|
1318
1483
|
return toForeignCallResult([toArray(returnValues)]);
|
|
1319
1484
|
}
|
|
1320
1485
|
|
|
1321
1486
|
// eslint-disable-next-line camelcase
|
|
1322
1487
|
async aztec_txe_publicCallNewFlow(
|
|
1323
|
-
|
|
1488
|
+
foreignFromIsSome: ForeignCallSingle,
|
|
1489
|
+
foreignFromValue: ForeignCallSingle,
|
|
1324
1490
|
foreignAddress: ForeignCallSingle,
|
|
1325
1491
|
foreignCalldata: ForeignCallArray,
|
|
1326
1492
|
foreignIsStaticCall: ForeignCallSingle,
|
|
1493
|
+
foreignGasSettings: ForeignCallArray,
|
|
1327
1494
|
) {
|
|
1328
|
-
const from = addressFromSingle(
|
|
1495
|
+
const from = fromSingle(foreignFromIsSome).toBool() ? addressFromSingle(foreignFromValue) : undefined;
|
|
1329
1496
|
const address = addressFromSingle(foreignAddress);
|
|
1330
1497
|
const calldata = fromArray(foreignCalldata);
|
|
1331
1498
|
const isStaticCall = fromSingle(foreignIsStaticCall).toBool();
|
|
1499
|
+
const gasSettings = GasSettings.fromFields(fromArray(foreignGasSettings));
|
|
1500
|
+
|
|
1501
|
+
const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
|
|
1502
|
+
const returnValues = await this.handlerAsTxe().publicCallNewFlow(
|
|
1503
|
+
from,
|
|
1504
|
+
address,
|
|
1505
|
+
calldata,
|
|
1506
|
+
isStaticCall,
|
|
1507
|
+
gasSettings,
|
|
1508
|
+
);
|
|
1509
|
+
|
|
1510
|
+
// TODO(F-335): Avoid doing the following call here.
|
|
1511
|
+
await this.stateHandler.cycleJob();
|
|
1332
1512
|
|
|
1333
|
-
|
|
1513
|
+
if (isStaticCall) {
|
|
1514
|
+
// See equivalent branch in `aztec_txe_privateCallNewFlow`.
|
|
1515
|
+
return { result: returnValues };
|
|
1516
|
+
}
|
|
1517
|
+
const { txHash } = await this.handlerAsTxe().getLastTxEffects();
|
|
1518
|
+
return { result: returnValues, txHash: txHash.hash };
|
|
1519
|
+
});
|
|
1334
1520
|
|
|
1335
|
-
// TODO(F-335): Avoid doing the following call here.
|
|
1336
|
-
await this.stateHandler.cycleJob();
|
|
1337
1521
|
return toForeignCallResult([toArray(returnValues)]);
|
|
1338
1522
|
}
|
|
1339
1523
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The TXE oracle version constants are used to check that the oracle interface used for tests is in sync between
|
|
3
|
+
* TXE and Aztec.nr. This is separate from the contract oracle version in `pxe/src/oracle_version.ts`, which covers
|
|
4
|
+
* oracles used during contract execution by PXE.
|
|
5
|
+
*
|
|
6
|
+
* The Noir counterparts are in `noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr`.
|
|
7
|
+
*/
|
|
8
|
+
export const TXE_ORACLE_VERSION_MAJOR = 1;
|
|
9
|
+
export const TXE_ORACLE_VERSION_MINOR = 0;
|