@aztec/txe 0.0.1-commit.5daedc8 → 0.0.1-commit.6201a7b05

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.
Files changed (78) hide show
  1. package/dest/constants.d.ts +3 -0
  2. package/dest/constants.d.ts.map +1 -0
  3. package/dest/constants.js +2 -0
  4. package/dest/index.d.ts +1 -1
  5. package/dest/index.d.ts.map +1 -1
  6. package/dest/index.js +91 -56
  7. package/dest/oracle/interfaces.d.ts +36 -29
  8. package/dest/oracle/interfaces.d.ts.map +1 -1
  9. package/dest/oracle/txe_oracle_public_context.d.ts +16 -16
  10. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  11. package/dest/oracle/txe_oracle_public_context.js +20 -22
  12. package/dest/oracle/txe_oracle_top_level_context.d.ts +44 -27
  13. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  14. package/dest/oracle/txe_oracle_top_level_context.js +245 -109
  15. package/dest/rpc_translator.d.ts +136 -78
  16. package/dest/rpc_translator.d.ts.map +1 -1
  17. package/dest/rpc_translator.js +545 -178
  18. package/dest/state_machine/archiver.d.ts +21 -52
  19. package/dest/state_machine/archiver.d.ts.map +1 -1
  20. package/dest/state_machine/archiver.js +67 -99
  21. package/dest/state_machine/dummy_p2p_client.d.ts +21 -15
  22. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  23. package/dest/state_machine/dummy_p2p_client.js +45 -25
  24. package/dest/state_machine/global_variable_builder.d.ts +11 -5
  25. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  26. package/dest/state_machine/global_variable_builder.js +21 -3
  27. package/dest/state_machine/index.d.ts +9 -7
  28. package/dest/state_machine/index.d.ts.map +1 -1
  29. package/dest/state_machine/index.js +45 -24
  30. package/dest/state_machine/mock_epoch_cache.d.ts +26 -8
  31. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  32. package/dest/state_machine/mock_epoch_cache.js +49 -9
  33. package/dest/state_machine/synchronizer.d.ts +6 -5
  34. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  35. package/dest/state_machine/synchronizer.js +8 -7
  36. package/dest/txe_session.d.ts +71 -15
  37. package/dest/txe_session.d.ts.map +1 -1
  38. package/dest/txe_session.js +228 -57
  39. package/dest/util/encoding.d.ts +688 -19
  40. package/dest/util/encoding.d.ts.map +1 -1
  41. package/dest/util/encoding.js +5 -1
  42. package/dest/util/txe_account_store.d.ts +10 -0
  43. package/dest/util/txe_account_store.d.ts.map +1 -0
  44. package/dest/util/{txe_account_data_provider.js → txe_account_store.js} +1 -1
  45. package/dest/util/txe_public_contract_data_source.d.ts +8 -8
  46. package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
  47. package/dest/util/txe_public_contract_data_source.js +13 -32
  48. package/dest/utils/block_creation.d.ts +21 -6
  49. package/dest/utils/block_creation.d.ts.map +1 -1
  50. package/dest/utils/block_creation.js +38 -4
  51. package/dest/utils/tx_effect_creation.d.ts +3 -3
  52. package/dest/utils/tx_effect_creation.d.ts.map +1 -1
  53. package/dest/utils/tx_effect_creation.js +4 -7
  54. package/package.json +18 -18
  55. package/src/constants.ts +3 -0
  56. package/src/index.ts +103 -63
  57. package/src/oracle/interfaces.ts +37 -33
  58. package/src/oracle/txe_oracle_public_context.ts +21 -28
  59. package/src/oracle/txe_oracle_top_level_context.ts +291 -151
  60. package/src/rpc_translator.ts +645 -206
  61. package/src/state_machine/archiver.ts +63 -117
  62. package/src/state_machine/dummy_p2p_client.ts +62 -33
  63. package/src/state_machine/global_variable_builder.ts +38 -6
  64. package/src/state_machine/index.ts +68 -22
  65. package/src/state_machine/mock_epoch_cache.ts +61 -14
  66. package/src/state_machine/synchronizer.ts +9 -8
  67. package/src/txe_session.ts +351 -105
  68. package/src/util/encoding.ts +6 -1
  69. package/src/util/{txe_account_data_provider.ts → txe_account_store.ts} +1 -1
  70. package/src/util/txe_public_contract_data_source.ts +20 -47
  71. package/src/utils/block_creation.ts +49 -15
  72. package/src/utils/tx_effect_creation.ts +5 -12
  73. package/dest/util/txe_account_data_provider.d.ts +0 -10
  74. package/dest/util/txe_account_data_provider.d.ts.map +0 -1
  75. package/dest/util/txe_contract_data_provider.d.ts +0 -12
  76. package/dest/util/txe_contract_data_provider.d.ts.map +0 -1
  77. package/dest/util/txe_contract_data_provider.js +0 -22
  78. package/src/util/txe_contract_data_provider.ts +0 -36
@@ -1,35 +1,41 @@
1
- import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT, DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
2
- import { Schnorr } from '@aztec/foundation/crypto';
3
- import { Fr } from '@aztec/foundation/fields';
1
+ import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
2
+ import { BlockNumber } from '@aztec/foundation/branded-types';
3
+ import { Schnorr } from '@aztec/foundation/crypto/schnorr';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
5
  import { LogLevels, applyStringFormatting, createLogger } from '@aztec/foundation/log';
5
6
  import { TestDateProvider } from '@aztec/foundation/timer';
6
- import { ORACLE_VERSION, enrichPublicSimulationError } from '@aztec/pxe/server';
7
+ import { CapsuleService, ORACLE_VERSION_MAJOR, enrichPublicSimulationError } from '@aztec/pxe/server';
7
8
  import { ExecutionNoteCache, ExecutionTaggingIndexCache, HashedValuesCache, Oracle, PrivateExecutionOracle, UtilityExecutionOracle, executePrivateFunction, generateSimulatedProvingResult } from '@aztec/pxe/simulator';
8
9
  import { ExecutionError, WASMSimulator, createSimulationError, extractCallStack, resolveAssertionMessageFromError, toACVMWitness, witnessMapToFields } from '@aztec/simulator/client';
9
- import { GuardedMerkleTreeOperations, PublicContractsDB, PublicProcessor, PublicTxSimulator } from '@aztec/simulator/server';
10
- import { FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
10
+ import { CppPublicTxSimulator, GuardedMerkleTreeOperations, PublicContractsDB, PublicProcessor } from '@aztec/simulator/server';
11
+ import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
11
12
  import { AuthWitness } from '@aztec/stdlib/auth-witness';
12
13
  import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
13
14
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
14
- import { Body, L2Block } from '@aztec/stdlib/block';
15
15
  import { computePartialAddress } from '@aztec/stdlib/contract';
16
- import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
16
+ import { FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT, Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
17
17
  import { computeCalldataHash, computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
18
18
  import { PartialPrivateTailPublicInputsForPublic, PrivateKernelTailCircuitPublicInputs, PrivateToPublicAccumulatedData, PublicCallRequest } from '@aztec/stdlib/kernel';
19
19
  import { ChonkProof } from '@aztec/stdlib/proofs';
20
- import { makeAppendOnlyTreeSnapshot, makeGlobalVariables } from '@aztec/stdlib/testing';
20
+ import { makeGlobalVariables } from '@aztec/stdlib/testing';
21
21
  import { MerkleTreeId } from '@aztec/stdlib/trees';
22
22
  import { CallContext, HashedValues, PrivateExecutionResult, Tx, TxConstantData, TxContext, TxEffect, TxHash, collectNested } from '@aztec/stdlib/tx';
23
23
  import { ForkCheckpoint } from '@aztec/world-state';
24
+ import { DEFAULT_ADDRESS } from '../constants.js';
24
25
  import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
25
- import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlockHeader } from '../utils/block_creation.js';
26
+ import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
26
27
  export class TXEOracleTopLevelContext {
27
28
  stateMachine;
28
- contractDataProvider;
29
+ contractStore;
30
+ noteStore;
29
31
  keyStore;
30
- addressDataProvider;
31
- accountDataProvider;
32
- pxeOracleInterface;
32
+ addressStore;
33
+ accountStore;
34
+ senderTaggingStore;
35
+ recipientTaggingStore;
36
+ senderAddressBookStore;
37
+ capsuleStore;
38
+ privateEventStore;
33
39
  nextBlockTimestamp;
34
40
  version;
35
41
  chainId;
@@ -37,13 +43,18 @@ export class TXEOracleTopLevelContext {
37
43
  isMisc;
38
44
  isTxe;
39
45
  logger;
40
- constructor(stateMachine, contractDataProvider, keyStore, addressDataProvider, accountDataProvider, pxeOracleInterface, nextBlockTimestamp, version, chainId, authwits){
46
+ constructor(stateMachine, contractStore, noteStore, keyStore, addressStore, accountStore, senderTaggingStore, recipientTaggingStore, senderAddressBookStore, capsuleStore, privateEventStore, nextBlockTimestamp, version, chainId, authwits){
41
47
  this.stateMachine = stateMachine;
42
- this.contractDataProvider = contractDataProvider;
48
+ this.contractStore = contractStore;
49
+ this.noteStore = noteStore;
43
50
  this.keyStore = keyStore;
44
- this.addressDataProvider = addressDataProvider;
45
- this.accountDataProvider = accountDataProvider;
46
- this.pxeOracleInterface = pxeOracleInterface;
51
+ this.addressStore = addressStore;
52
+ this.accountStore = accountStore;
53
+ this.senderTaggingStore = senderTaggingStore;
54
+ this.recipientTaggingStore = recipientTaggingStore;
55
+ this.senderAddressBookStore = senderAddressBookStore;
56
+ this.capsuleStore = capsuleStore;
57
+ this.privateEventStore = privateEventStore;
47
58
  this.nextBlockTimestamp = nextBlockTimestamp;
48
59
  this.version = version;
49
60
  this.chainId = chainId;
@@ -53,37 +64,52 @@ export class TXEOracleTopLevelContext {
53
64
  this.logger = createLogger('txe:top_level_context');
54
65
  this.logger.debug('Entering Top Level Context');
55
66
  }
56
- utilityAssertCompatibleOracleVersion(version) {
57
- if (version !== ORACLE_VERSION) {
58
- throw new Error(`Incompatible oracle version. TXE is using version '${ORACLE_VERSION}', but got a request for '${version}'.`);
67
+ contractOracleVersion;
68
+ assertCompatibleOracleVersion(major, minor) {
69
+ if (major !== ORACLE_VERSION_MAJOR) {
70
+ const hint = major > ORACLE_VERSION_MAJOR ? 'The contract was compiled with a newer version of Aztec.nr than this aztec cli version supports. Upgrade your aztec cli version to a compatible version.' : 'The contract was compiled with an older version of Aztec.nr than this aztec cli version supports. Recompile the contract with a compatible version of Aztec.nr.';
71
+ throw new Error(`Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`);
59
72
  }
73
+ this.contractOracleVersion = {
74
+ major,
75
+ minor
76
+ };
77
+ }
78
+ // Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
79
+ nonOracleFunctionGetContractOracleVersion() {
80
+ return this.contractOracleVersion;
60
81
  }
61
82
  // This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
62
83
  // setup.
63
- utilityGetRandomField() {
84
+ getRandomField() {
64
85
  return Fr.random();
65
86
  }
66
87
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
67
- utilityDebugLog(level, message, fields) {
88
+ log(level, message, fields) {
68
89
  if (!LogLevels[level]) {
69
- throw new Error(`Invalid debug log level: ${level}`);
90
+ throw new Error(`Invalid log level: ${level}`);
70
91
  }
71
92
  const levelName = LogLevels[level];
72
93
  this.logger[levelName](`${applyStringFormatting(message, fields)}`, {
73
94
  module: `${this.logger.module}:debug_log`
74
95
  });
96
+ return Promise.resolve();
97
+ }
98
+ getDefaultAddress() {
99
+ return DEFAULT_ADDRESS;
75
100
  }
76
- async txeGetNextBlockNumber() {
77
- return await this.getLastBlockNumber() + 1;
101
+ async getNextBlockNumber() {
102
+ return BlockNumber(await this.getLastBlockNumber() + 1);
78
103
  }
79
- txeGetNextBlockTimestamp() {
104
+ getNextBlockTimestamp() {
80
105
  return Promise.resolve(this.nextBlockTimestamp);
81
106
  }
82
- async txeGetLastBlockTimestamp() {
107
+ async getLastBlockTimestamp() {
83
108
  return (await this.stateMachine.node.getBlockHeader('latest')).globalVariables.timestamp;
84
109
  }
85
- async txeGetLastTxEffects() {
86
- const block = await this.stateMachine.archiver.getBlock('latest');
110
+ async getLastTxEffects() {
111
+ const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
112
+ const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
87
113
  if (block.body.txEffects.length != 1) {
88
114
  // Note that calls like env.mine() will result in blocks with no transactions, hitting this
89
115
  throw new Error(`Expected a single transaction in the last block, found ${block.body.txEffects.length}`);
@@ -95,17 +121,39 @@ export class TXEOracleTopLevelContext {
95
121
  nullifiers: txEffects.nullifiers
96
122
  };
97
123
  }
98
- async txeAdvanceBlocksBy(blocks) {
124
+ async syncContractNonOracleMethod(contractAddress, scope, jobId) {
125
+ if (contractAddress.equals(DEFAULT_ADDRESS)) {
126
+ this.logger.debug(`Skipping sync in getPrivateEvents because the events correspond to the default address.`);
127
+ return;
128
+ }
129
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
130
+ await this.stateMachine.contractSyncService.ensureContractSynced(contractAddress, null, async (call, execScopes)=>{
131
+ await this.executeUtilityCall(call, execScopes, jobId);
132
+ }, blockHeader, jobId, [
133
+ scope
134
+ ]);
135
+ }
136
+ async getPrivateEvents(selector, contractAddress, scope) {
137
+ return (await this.privateEventStore.getPrivateEvents(selector, {
138
+ contractAddress,
139
+ scopes: [
140
+ scope
141
+ ],
142
+ fromBlock: 0,
143
+ toBlock: await this.getLastBlockNumber() + 1
144
+ })).map((e)=>e.packedEvent);
145
+ }
146
+ async advanceBlocksBy(blocks) {
99
147
  this.logger.debug(`time traveling ${blocks} blocks`);
100
148
  for(let i = 0; i < blocks; i++){
101
149
  await this.mineBlock();
102
150
  }
103
151
  }
104
- txeAdvanceTimestampBy(duration) {
152
+ advanceTimestampBy(duration) {
105
153
  this.logger.debug(`time traveling ${duration} seconds`);
106
154
  this.nextBlockTimestamp += duration;
107
155
  }
108
- async txeDeploy(artifact, instance, secret) {
156
+ async deploy(artifact, instance, secret) {
109
157
  // Emit deployment nullifier
110
158
  await this.mineBlock({
111
159
  nullifiers: [
@@ -113,34 +161,34 @@ export class TXEOracleTopLevelContext {
113
161
  ]
114
162
  });
115
163
  if (!secret.equals(Fr.ZERO)) {
116
- await this.txeAddAccount(artifact, instance, secret);
164
+ await this.addAccount(artifact, instance, secret);
117
165
  } else {
118
- await this.contractDataProvider.addContractInstance(instance);
119
- await this.contractDataProvider.addContractArtifact(instance.currentContractClassId, artifact);
166
+ await this.contractStore.addContractInstance(instance);
167
+ await this.contractStore.addContractArtifact(artifact);
120
168
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
121
169
  }
122
170
  }
123
- async txeAddAccount(artifact, instance, secret) {
171
+ async addAccount(artifact, instance, secret) {
124
172
  const partialAddress = await computePartialAddress(instance);
125
173
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
126
- await this.contractDataProvider.addContractInstance(instance);
127
- await this.contractDataProvider.addContractArtifact(instance.currentContractClassId, artifact);
174
+ await this.contractStore.addContractInstance(instance);
175
+ await this.contractStore.addContractArtifact(artifact);
128
176
  const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
129
- await this.accountDataProvider.setAccount(completeAddress.address, completeAddress);
130
- await this.addressDataProvider.addCompleteAddress(completeAddress);
177
+ await this.accountStore.setAccount(completeAddress.address, completeAddress);
178
+ await this.addressStore.addCompleteAddress(completeAddress);
131
179
  this.logger.debug(`Created account ${completeAddress.address}`);
132
180
  return completeAddress;
133
181
  }
134
- async txeCreateAccount(secret) {
135
- // This is a footgun !
182
+ async createAccount(secret) {
183
+ // This is a foot gun !
136
184
  const completeAddress = await this.keyStore.addAccount(secret, secret);
137
- await this.accountDataProvider.setAccount(completeAddress.address, completeAddress);
138
- await this.addressDataProvider.addCompleteAddress(completeAddress);
185
+ await this.accountStore.setAccount(completeAddress.address, completeAddress);
186
+ await this.addressStore.addCompleteAddress(completeAddress);
139
187
  this.logger.debug(`Created account ${completeAddress.address}`);
140
188
  return completeAddress;
141
189
  }
142
- async txeAddAuthWitness(address, messageHash) {
143
- const account = await this.accountDataProvider.getAccount(address);
190
+ async addAuthWitness(address, messageHash) {
191
+ const account = await this.accountStore.getAccount(address);
144
192
  const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
145
193
  const schnorr = new Schnorr();
146
194
  const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
@@ -150,7 +198,7 @@ export class TXEOracleTopLevelContext {
150
198
  this.authwits.set(authWitness.requestHash.toString(), authWitness);
151
199
  }
152
200
  async mineBlock(options = {}) {
153
- const blockNumber = await this.txeGetNextBlockNumber();
201
+ const blockNumber = await this.getNextBlockNumber();
154
202
  const txEffect = TxEffect.empty();
155
203
  txEffect.nullifiers = [
156
204
  getSingleTxBlockRequestHash(blockNumber),
@@ -159,42 +207,87 @@ export class TXEOracleTopLevelContext {
159
207
  txEffect.txHash = new TxHash(new Fr(blockNumber));
160
208
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
161
209
  await insertTxEffectIntoWorldTrees(txEffect, forkedWorldTrees);
162
- const block = new L2Block(makeAppendOnlyTreeSnapshot(), await makeTXEBlockHeader(forkedWorldTrees, makeGlobalVariables(undefined, {
210
+ const globals = makeGlobalVariables(undefined, {
163
211
  blockNumber,
164
212
  timestamp: this.nextBlockTimestamp,
165
213
  version: this.version,
166
214
  chainId: this.chainId
167
- })), new Body([
215
+ });
216
+ const block = await makeTXEBlock(forkedWorldTrees, globals, [
168
217
  txEffect
169
- ]));
218
+ ]);
170
219
  await forkedWorldTrees.close();
171
220
  this.logger.info(`Created block ${blockNumber} with timestamp ${block.header.globalVariables.timestamp}`);
172
221
  await this.stateMachine.handleL2Block(block);
173
222
  }
174
- async txePrivateCallNewFlow(from, targetContractAddress = AztecAddress.zero(), functionSelector = FunctionSelector.empty(), args, argsHash = Fr.zero(), isStaticCall = false) {
175
- this.logger.verbose(`Executing external function ${await this.contractDataProvider.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`);
176
- const artifact = await this.contractDataProvider.getFunctionArtifact(targetContractAddress, functionSelector);
223
+ async privateCallNewFlow(from, targetContractAddress = AztecAddress.zero(), functionSelector = FunctionSelector.empty(), args, argsHash = Fr.zero(), isStaticCall = false, jobId) {
224
+ this.logger.verbose(`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`);
225
+ const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
177
226
  if (!artifact) {
178
227
  const message = functionSelector.equals(await FunctionSelector.fromSignature('verify_private_authwit(Field)')) ? 'Found no account contract artifact for a private authwit check - use `create_contract_account` instead of `create_light_account` for authwit support.' : 'Function Artifact does not exist';
179
228
  throw new Error(message);
180
229
  }
181
- const blockNumber = await this.txeGetNextBlockNumber();
230
+ // When `from` is the zero address (e.g. when deploying a new account contract), we return an
231
+ // empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
232
+ const effectiveScopes = from.isZero() ? [] : [
233
+ from
234
+ ];
235
+ // Sync notes before executing private function to discover notes from previous transactions
236
+ const utilityExecutor = async (call, execScopes)=>{
237
+ await this.executeUtilityCall(call, execScopes, jobId);
238
+ };
239
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
240
+ await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, utilityExecutor, blockHeader, jobId, effectiveScopes);
241
+ const blockNumber = await this.getNextBlockNumber();
182
242
  const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
183
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
184
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
243
+ const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
244
+ const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
185
245
  const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
186
246
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
187
- const blockHeader = await this.pxeOracleInterface.getAnchorBlockHeader();
188
247
  const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
189
248
  const noteCache = new ExecutionNoteCache(protocolNullifier);
249
+ // In production, the account contract sets the min revertible counter before calling the app function.
250
+ // Since TXE bypasses the account contract, we simulate this by setting minRevertibleSideEffectCounter to 1,
251
+ // marking all side effects as revertible.
252
+ const minRevertibleSideEffectCounter = 1;
253
+ await noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
190
254
  const taggingIndexCache = new ExecutionTaggingIndexCache();
191
255
  const simulator = new WASMSimulator();
192
- const privateExecutionOracle = new PrivateExecutionOracle(argsHash, txContext, callContext, /** Header of a block whose state is used during private execution (not the block the transaction is included in). */ blockHeader, /** List of transient auth witnesses to be used during this simulation */ Array.from(this.authwits.values()), /** List of transient auth witnesses to be used during this simulation */ [], HashedValuesCache.create([
193
- new HashedValues(args, argsHash)
194
- ]), noteCache, taggingIndexCache, this.pxeOracleInterface, 0, 1, undefined, undefined, /**
195
- * In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
196
- * contract would perform, including setting senderForTags.
197
- */ from, simulator);
256
+ const privateExecutionOracle = new PrivateExecutionOracle({
257
+ argsHash,
258
+ txContext,
259
+ callContext,
260
+ anchorBlockHeader: blockHeader,
261
+ utilityExecutor,
262
+ authWitnesses: Array.from(this.authwits.values()),
263
+ capsules: [],
264
+ executionCache: HashedValuesCache.create([
265
+ new HashedValues(args, argsHash)
266
+ ]),
267
+ noteCache,
268
+ taggingIndexCache,
269
+ contractStore: this.contractStore,
270
+ noteStore: this.noteStore,
271
+ keyStore: this.keyStore,
272
+ addressStore: this.addressStore,
273
+ aztecNode: this.stateMachine.node,
274
+ senderTaggingStore: this.senderTaggingStore,
275
+ recipientTaggingStore: this.recipientTaggingStore,
276
+ senderAddressBookStore: this.senderAddressBookStore,
277
+ capsuleService: new CapsuleService(this.capsuleStore, effectiveScopes),
278
+ privateEventStore: this.privateEventStore,
279
+ contractSyncService: this.stateMachine.contractSyncService,
280
+ jobId,
281
+ totalPublicCalldataCount: 0,
282
+ sideEffectCounter: minRevertibleSideEffectCounter,
283
+ scopes: effectiveScopes,
284
+ // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
285
+ // contract would perform, including setting senderForTags.
286
+ senderForTags: from,
287
+ simulator,
288
+ messageContextService: this.stateMachine.messageContextService,
289
+ l2TipsStore: this.stateMachine.node
290
+ });
198
291
  // 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.
199
292
  let result;
200
293
  let executionResult;
@@ -206,21 +299,19 @@ export class TXEOracleTopLevelContext {
206
299
  r.publicInputs.publicTeardownCallRequest
207
300
  ]));
208
301
  const publicFunctionsCalldata = await Promise.all(publicCallRequests.map(async (r)=>{
209
- const calldata = await privateExecutionOracle.privateLoadFromExecutionCache(r.calldataHash);
302
+ const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
210
303
  return new HashedValues(calldata, r.calldataHash);
211
304
  }));
212
- // TXE's top level context does not track side effect counters, and as such, minRevertibleSideEffectCounter is always 0.
213
- // This has the unfortunate consequence of always producing revertible nullifiers, which means we
214
- // must set the firstNullifierHint to Fr.ZERO so the txRequestHash is always used as nonce generator
215
- result = new PrivateExecutionResult(executionResult, Fr.ZERO, publicFunctionsCalldata);
305
+ noteCache.finish();
306
+ const nonceGenerator = noteCache.getNonceGenerator();
307
+ result = new PrivateExecutionResult(executionResult, nonceGenerator, publicFunctionsCalldata);
216
308
  } catch (err) {
217
309
  throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
218
310
  }
219
- // According to the protocol rules, the nonce generator for the note hashes
220
- // can either be the first nullifier in the tx or the hash of the initial tx request
221
- // if there are none.
222
- const nonceGenerator = result.firstNullifier.equals(Fr.ZERO) ? protocolNullifier : result.firstNullifier;
223
- const { publicInputs } = await generateSimulatedProvingResult(result, nonceGenerator, this.contractDataProvider);
311
+ // According to the protocol rules, there must be at least one nullifier in the tx. The first nullifier is used as
312
+ // the nonce generator for the note hashes.
313
+ // We pass the non-zero minRevertibleSideEffectCounter to make sure the side effects are split correctly.
314
+ const { publicInputs } = await generateSimulatedProvingResult(result, (addr, sel)=>this.contractStore.getDebugFunctionName(addr, sel), this.stateMachine.node, minRevertibleSideEffectCounter);
224
315
  const globals = makeGlobalVariables();
225
316
  globals.blockNumber = blockNumber;
226
317
  globals.timestamp = this.nextBlockTimestamp;
@@ -228,7 +319,8 @@ export class TXEOracleTopLevelContext {
228
319
  globals.version = this.version;
229
320
  globals.gasFees = GasFees.empty();
230
321
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
231
- const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractDataProvider));
322
+ const bindings = this.logger.getBindings();
323
+ const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore), bindings);
232
324
  const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
233
325
  const config = PublicSimulatorConfig.from({
234
326
  skipFeeEnforcement: true,
@@ -237,7 +329,7 @@ export class TXEOracleTopLevelContext {
237
329
  collectStatistics: false,
238
330
  collectCallMetadata: true
239
331
  });
240
- const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, new PublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config), new TestDateProvider());
332
+ const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings), new TestDateProvider(), undefined, createLogger('simulator:public-processor', bindings));
241
333
  const tx = await Tx.create({
242
334
  data: publicInputs,
243
335
  chonkProof: ChonkProof.empty(),
@@ -258,7 +350,7 @@ export class TXEOracleTopLevelContext {
258
350
  } else if (!processedTx.revertCode.isOK()) {
259
351
  if (processedTx.revertReason) {
260
352
  try {
261
- await enrichPublicSimulationError(processedTx.revertReason, this.contractDataProvider, this.logger);
353
+ await enrichPublicSimulationError(processedTx.revertReason, this.contractStore, this.logger);
262
354
  // eslint-disable-next-line no-empty
263
355
  } catch {}
264
356
  throw new Error(`Contract execution has reverted: ${processedTx.revertReason.getMessage()}`);
@@ -266,10 +358,20 @@ export class TXEOracleTopLevelContext {
266
358
  throw new Error('Contract execution has reverted');
267
359
  }
268
360
  }
361
+ // Walk the nested private-call tree and collect every offchain effect the transaction emitted.
362
+ // PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
363
+ // `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
364
+ // we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
365
+ const offchainEffects = collectNested([
366
+ executionResult
367
+ ], (r)=>r.offchainEffects.map((e)=>e.data));
269
368
  if (isStaticCall) {
270
369
  await checkpoint.revert();
271
370
  await forkedWorldTrees.close();
272
- return executionResult.returnValues ?? [];
371
+ return {
372
+ returnValues: executionResult.returnValues ?? [],
373
+ offchainEffects
374
+ };
273
375
  }
274
376
  const txEffect = TxEffect.empty();
275
377
  txEffect.noteHashes = processedTx.txEffect.noteHashes;
@@ -280,22 +382,24 @@ export class TXEOracleTopLevelContext {
280
382
  txEffect.txHash = new TxHash(new Fr(blockNumber));
281
383
  const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
282
384
  await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
283
- const body = new Body([
385
+ const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [
284
386
  txEffect
285
387
  ]);
286
- const l2Block = new L2Block(makeAppendOnlyTreeSnapshot(), await makeTXEBlockHeader(forkedWorldTrees, globals), body);
287
388
  await this.stateMachine.handleL2Block(l2Block);
288
389
  await forkedWorldTrees.close();
289
- return executionResult.returnValues ?? [];
390
+ return {
391
+ returnValues: executionResult.returnValues ?? [],
392
+ offchainEffects
393
+ };
290
394
  }
291
- async txePublicCallNewFlow(from, targetContractAddress, calldata, isStaticCall) {
292
- this.logger.verbose(`Executing public function ${await this.contractDataProvider.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`);
293
- const blockNumber = await this.txeGetNextBlockNumber();
294
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
295
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
395
+ async publicCallNewFlow(from, targetContractAddress, calldata, isStaticCall) {
396
+ this.logger.verbose(`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`);
397
+ const blockNumber = await this.getNextBlockNumber();
398
+ const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
399
+ const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
296
400
  const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
297
401
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
298
- const anchorBlockHeader = await this.pxeOracleInterface.getAnchorBlockHeader();
402
+ const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
299
403
  const calldataHash = await computeCalldataHash(calldata);
300
404
  const calldataHashedValues = new HashedValues(calldata, calldataHash);
301
405
  const globals = makeGlobalVariables();
@@ -305,7 +409,8 @@ export class TXEOracleTopLevelContext {
305
409
  globals.version = this.version;
306
410
  globals.gasFees = GasFees.empty();
307
411
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
308
- const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractDataProvider));
412
+ const bindings2 = this.logger.getBindings();
413
+ const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore), bindings2);
309
414
  const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
310
415
  const config = PublicSimulatorConfig.from({
311
416
  skipFeeEnforcement: true,
@@ -314,23 +419,21 @@ export class TXEOracleTopLevelContext {
314
419
  collectStatistics: false,
315
420
  collectCallMetadata: true
316
421
  });
317
- const simulator = new PublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config);
318
- const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, simulator, new TestDateProvider());
422
+ const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings2);
423
+ const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, simulator, new TestDateProvider(), undefined, createLogger('simulator:public-processor', bindings2));
319
424
  // We're simulating a scenario in which private execution immediately enqueues a public call and halts. The private
320
425
  // kernel init would in this case inject a nullifier with the transaction request hash as a non-revertible
321
426
  // side-effect, which the AVM then expects to exist in order to use it as the nonce generator when siloing notes as
322
427
  // unique.
323
428
  const nonRevertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
324
- if (!isStaticCall) {
325
- nonRevertibleAccumulatedData.nullifiers[0] = getSingleTxBlockRequestHash(blockNumber);
326
- }
429
+ nonRevertibleAccumulatedData.nullifiers[0] = getSingleTxBlockRequestHash(blockNumber);
327
430
  // The enqueued public call itself we make be revertible so that the public execution is itself revertible, as tests
328
431
  // may require producing reverts.
329
432
  const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
330
433
  revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(from, targetContractAddress, isStaticCall, calldataHash);
331
434
  const inputsForPublic = new PartialPrivateTailPublicInputsForPublic(nonRevertibleAccumulatedData, revertibleAccumulatedData, PublicCallRequest.empty());
332
435
  const constantData = new TxConstantData(anchorBlockHeader, txContext, Fr.zero(), Fr.zero());
333
- const txData = new PrivateKernelTailCircuitPublicInputs(constantData, /*gasUsed=*/ new Gas(0, 0), /*feePayer=*/ AztecAddress.zero(), /*includeByTimestamp=*/ 0n, inputsForPublic, undefined);
436
+ const txData = new PrivateKernelTailCircuitPublicInputs(constantData, /*gasUsed=*/ new Gas(0, 0), /*feePayer=*/ AztecAddress.zero(), /*expirationTimestamp=*/ 0n, inputsForPublic, undefined);
334
437
  const tx = await Tx.create({
335
438
  data: txData,
336
439
  chonkProof: ChonkProof.empty(),
@@ -353,7 +456,7 @@ export class TXEOracleTopLevelContext {
353
456
  } else if (!processedTx.revertCode.isOK()) {
354
457
  if (processedTx.revertReason) {
355
458
  try {
356
- await enrichPublicSimulationError(processedTx.revertReason, this.contractDataProvider, this.logger);
459
+ await enrichPublicSimulationError(processedTx.revertReason, this.contractStore, this.logger);
357
460
  // eslint-disable-next-line no-empty
358
461
  } catch {}
359
462
  throw new Error(`Contract execution has reverted: ${processedTx.revertReason.getMessage()}`);
@@ -376,25 +479,37 @@ export class TXEOracleTopLevelContext {
376
479
  txEffect.txHash = new TxHash(new Fr(blockNumber));
377
480
  const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
378
481
  await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
379
- const body = new Body([
482
+ const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [
380
483
  txEffect
381
484
  ]);
382
- const l2Block = new L2Block(makeAppendOnlyTreeSnapshot(), await makeTXEBlockHeader(forkedWorldTrees, globals), body);
383
485
  await this.stateMachine.handleL2Block(l2Block);
384
486
  await forkedWorldTrees.close();
385
487
  return returnValues ?? [];
386
488
  }
387
- async txeSimulateUtilityFunction(targetContractAddress, functionSelector, args) {
388
- const artifact = await this.contractDataProvider.getFunctionArtifact(targetContractAddress, functionSelector);
489
+ async executeUtilityFunction(targetContractAddress, functionSelector, args, jobId) {
490
+ const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
389
491
  if (!artifact) {
390
492
  throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
391
493
  }
392
- const call = {
494
+ // Sync notes before executing utility function to discover notes from previous transactions
495
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
496
+ await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, async (call, execScopes)=>{
497
+ await this.executeUtilityCall(call, execScopes, jobId);
498
+ }, blockHeader, jobId, await this.keyStore.getAccounts());
499
+ const call = FunctionCall.from({
393
500
  name: artifact.name,
501
+ to: targetContractAddress,
394
502
  selector: functionSelector,
395
- to: targetContractAddress
396
- };
397
- const entryPointArtifact = await this.pxeOracleInterface.getFunctionArtifact(call.to, call.selector);
503
+ type: FunctionType.UTILITY,
504
+ hideMsgSender: false,
505
+ isStatic: false,
506
+ args,
507
+ returnTypes: []
508
+ });
509
+ return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId);
510
+ }
511
+ async executeUtilityCall(call, scopes, jobId) {
512
+ const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
398
513
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
399
514
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
400
515
  }
@@ -403,8 +518,28 @@ export class TXEOracleTopLevelContext {
403
518
  selector: call.selector
404
519
  });
405
520
  try {
406
- const oracle = new UtilityExecutionOracle(call.to, [], [], this.pxeOracleInterface);
407
- const acirExecutionResult = await new WASMSimulator().executeUserCircuit(toACVMWitness(0, args), entryPointArtifact, new Oracle(oracle).toACIRCallback()).catch((err)=>{
521
+ const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
522
+ const oracle = new UtilityExecutionOracle({
523
+ contractAddress: call.to,
524
+ authWitnesses: [],
525
+ capsules: [],
526
+ anchorBlockHeader,
527
+ contractStore: this.contractStore,
528
+ noteStore: this.noteStore,
529
+ keyStore: this.keyStore,
530
+ addressStore: this.addressStore,
531
+ aztecNode: this.stateMachine.node,
532
+ recipientTaggingStore: this.recipientTaggingStore,
533
+ senderAddressBookStore: this.senderAddressBookStore,
534
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
535
+ privateEventStore: this.privateEventStore,
536
+ messageContextService: this.stateMachine.messageContextService,
537
+ contractSyncService: this.stateMachine.contractSyncService,
538
+ l2TipsStore: this.stateMachine.node,
539
+ jobId,
540
+ scopes
541
+ });
542
+ const acirExecutionResult = await new WASMSimulator().executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback()).catch((err)=>{
408
543
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
409
544
  throw new ExecutionError(err.message, {
410
545
  contractAddress: call.to,
@@ -413,10 +548,10 @@ export class TXEOracleTopLevelContext {
413
548
  cause: err
414
549
  });
415
550
  });
416
- this.logger.verbose(`Utility simulation for ${call.to}.${call.selector} completed`);
551
+ this.logger.verbose(`Utility execution for ${call.to}.${call.selector} completed`);
417
552
  return witnessMapToFields(acirExecutionResult.returnWitness);
418
553
  } catch (err) {
419
- throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility simulation'));
554
+ throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility execution'));
420
555
  }
421
556
  }
422
557
  close() {
@@ -427,6 +562,7 @@ export class TXEOracleTopLevelContext {
427
562
  ];
428
563
  }
429
564
  async getLastBlockNumber() {
430
- return (await this.stateMachine.node.getBlockHeader('latest'))?.globalVariables.blockNumber ?? 0;
565
+ const header = await this.stateMachine.node.getBlockHeader('latest');
566
+ return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
431
567
  }
432
568
  }