@aztec/txe 0.0.1-commit.001888fc → 0.0.1-commit.017a351

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 (148) hide show
  1. package/dest/ContractClassRegistry-PII7Q556.js +3 -0
  2. package/dest/ContractClassRegistry-PII7Q556.js.map +7 -0
  3. package/dest/ContractInstanceRegistry-3MHLGFNZ.js +3 -0
  4. package/dest/ContractInstanceRegistry-3MHLGFNZ.js.map +7 -0
  5. package/dest/FeeJuice-TQV5AQK7.js +3 -0
  6. package/dest/FeeJuice-TQV5AQK7.js.map +7 -0
  7. package/dest/MultiCallEntrypoint-SBJUIJY7.js +3 -0
  8. package/dest/MultiCallEntrypoint-SBJUIJY7.js.map +7 -0
  9. package/dest/SchnorrAccount-MQXPIZ44.js +3 -0
  10. package/dest/SchnorrAccount-MQXPIZ44.js.map +7 -0
  11. package/dest/bin/check_txe_oracle_version.d.ts +2 -0
  12. package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
  13. package/dest/bin/check_txe_oracle_version.js +61 -0
  14. package/dest/bin/index.js +3 -30
  15. package/dest/bin/index.js.map +7 -0
  16. package/dest/bin/oracle_test_server.d.ts +3 -0
  17. package/dest/bin/oracle_test_server.d.ts.map +1 -0
  18. package/dest/bin/oracle_test_server.js +41 -0
  19. package/dest/chunk-BA54BEZH.js +265 -0
  20. package/dest/chunk-BA54BEZH.js.map +7 -0
  21. package/dest/chunk-BJVAAXNA.js +3 -0
  22. package/dest/chunk-BJVAAXNA.js.map +7 -0
  23. package/dest/chunk-ZKW35DT6.js +304 -0
  24. package/dest/chunk-ZKW35DT6.js.map +7 -0
  25. package/dest/constants.d.ts +5 -1
  26. package/dest/constants.d.ts.map +1 -1
  27. package/dest/constants.js +8 -0
  28. package/dest/dispatcher_pool.d.ts +67 -0
  29. package/dest/dispatcher_pool.d.ts.map +1 -0
  30. package/dest/dispatcher_pool.js +284 -0
  31. package/dest/index.d.ts +185 -7
  32. package/dest/index.d.ts.map +1 -1
  33. package/dest/index.js +70 -190
  34. package/dest/metafile.json +38691 -0
  35. package/dest/msgpackr_fr_extension.d.ts +2 -0
  36. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  37. package/dest/msgpackr_fr_extension.js +21 -0
  38. package/dest/oracle/interfaces.d.ts +33 -8
  39. package/dest/oracle/interfaces.d.ts.map +1 -1
  40. package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
  41. package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
  42. package/dest/oracle/test-resolver/fixtures.js +39 -0
  43. package/dest/oracle/test-resolver/index.d.ts +9 -0
  44. package/dest/oracle/test-resolver/index.d.ts.map +1 -0
  45. package/dest/oracle/test-resolver/index.js +33 -0
  46. package/dest/oracle/test-resolver/resolver.d.ts +34 -0
  47. package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
  48. package/dest/oracle/test-resolver/resolver.js +114 -0
  49. package/dest/oracle/txe_oracle_public_context.d.ts +26 -2
  50. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  51. package/dest/oracle/txe_oracle_public_context.js +43 -1
  52. package/dest/oracle/txe_oracle_registry.d.ts +147 -0
  53. package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
  54. package/dest/oracle/txe_oracle_registry.js +562 -0
  55. package/dest/oracle/txe_oracle_top_level_context.d.ts +32 -12
  56. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  57. package/dest/oracle/txe_oracle_top_level_context.js +146 -54
  58. package/dest/oracle/txe_oracle_version.d.ts +17 -0
  59. package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
  60. package/dest/oracle/txe_oracle_version.js +14 -0
  61. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  62. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  63. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  64. package/dest/rpc_server.d.ts +14 -0
  65. package/dest/rpc_server.d.ts.map +1 -0
  66. package/dest/rpc_server.js +78 -0
  67. package/dest/rpc_translator.d.ts +96 -230
  68. package/dest/rpc_translator.d.ts.map +1 -1
  69. package/dest/rpc_translator.js +708 -617
  70. package/dest/server.bundle.js +3 -0
  71. package/dest/server.bundle.js.map +7 -0
  72. package/dest/state_machine/archiver.d.ts +4 -3
  73. package/dest/state_machine/archiver.d.ts.map +1 -1
  74. package/dest/state_machine/archiver.js +26 -15
  75. package/dest/state_machine/dummy_p2p_client.d.ts +13 -7
  76. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  77. package/dest/state_machine/dummy_p2p_client.js +16 -4
  78. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  79. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  80. package/dest/state_machine/global_variable_builder.js +9 -3
  81. package/dest/state_machine/index.d.ts +4 -2
  82. package/dest/state_machine/index.d.ts.map +1 -1
  83. package/dest/state_machine/index.js +17 -3
  84. package/dest/state_machine/mock_epoch_cache.d.ts +16 -3
  85. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  86. package/dest/state_machine/mock_epoch_cache.js +29 -2
  87. package/dest/state_machine/synchronizer.js +1 -1
  88. package/dest/txe_session.d.ts +74 -12
  89. package/dest/txe_session.d.ts.map +1 -1
  90. package/dest/txe_session.js +171 -36
  91. package/dest/utils/encoding.d.ts +191 -0
  92. package/dest/utils/encoding.d.ts.map +1 -0
  93. package/dest/{util → utils}/encoding.js +7 -2
  94. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  95. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  96. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  97. package/dest/utils/txe_account_store.d.ts.map +1 -0
  98. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  99. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  100. package/dest/utils/txe_artifact_resolver.js +161 -0
  101. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  102. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  103. package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
  104. package/dest/worker.bundle.js +3 -0
  105. package/dest/worker.bundle.js.map +7 -0
  106. package/dest/worker.d.ts +2 -0
  107. package/dest/worker.d.ts.map +1 -0
  108. package/dest/worker.js +92 -0
  109. package/package.json +38 -21
  110. package/src/bin/check_txe_oracle_version.ts +70 -0
  111. package/src/bin/index.ts +11 -2
  112. package/src/bin/oracle_test_server.ts +51 -0
  113. package/src/constants.ts +10 -0
  114. package/src/dispatcher_pool.ts +316 -0
  115. package/src/index.ts +97 -227
  116. package/src/msgpackr_fr_extension.ts +23 -0
  117. package/src/oracle/interfaces.ts +29 -7
  118. package/src/oracle/test-resolver/fixtures.ts +84 -0
  119. package/src/oracle/test-resolver/index.ts +45 -0
  120. package/src/oracle/test-resolver/resolver.ts +165 -0
  121. package/src/oracle/txe_oracle_public_context.ts +60 -0
  122. package/src/oracle/txe_oracle_registry.ts +401 -0
  123. package/src/oracle/txe_oracle_top_level_context.ts +177 -61
  124. package/src/oracle/txe_oracle_version.ts +17 -0
  125. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  126. package/src/rpc_server.ts +87 -0
  127. package/src/rpc_translator.ts +789 -904
  128. package/src/state_machine/archiver.ts +38 -16
  129. package/src/state_machine/dummy_p2p_client.ts +31 -11
  130. package/src/state_machine/global_variable_builder.ts +18 -3
  131. package/src/state_machine/index.ts +20 -5
  132. package/src/state_machine/mock_epoch_cache.ts +38 -3
  133. package/src/state_machine/synchronizer.ts +1 -1
  134. package/src/txe_session.ts +281 -38
  135. package/src/{util → utils}/encoding.ts +8 -2
  136. package/src/utils/txe_artifact_resolver.ts +217 -0
  137. package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
  138. package/src/worker.ts +98 -0
  139. package/dest/util/encoding.d.ts +0 -720
  140. package/dest/util/encoding.d.ts.map +0 -1
  141. package/dest/util/expected_failure_error.d.ts.map +0 -1
  142. package/dest/util/txe_account_store.d.ts.map +0 -1
  143. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  144. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  145. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  146. /package/dest/{util → utils}/txe_account_store.js +0 -0
  147. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  148. /package/src/{util → utils}/txe_account_store.ts +0 -0
@@ -1,10 +1,10 @@
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';
1
+ import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, MAX_PRIVATE_LOGS_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
2
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
3
3
  import { Schnorr } from '@aztec/foundation/crypto/schnorr';
4
4
  import { Fr } from '@aztec/foundation/curves/bn254';
5
5
  import { LogLevels, applyStringFormatting, createLogger } from '@aztec/foundation/log';
6
6
  import { TestDateProvider } from '@aztec/foundation/timer';
7
- import { ORACLE_VERSION, enrichPublicSimulationError } from '@aztec/pxe/server';
7
+ import { CapsuleService, ORACLE_VERSION_MAJOR, composeHooks, enrichPublicSimulationError } from '@aztec/pxe/server';
8
8
  import { ExecutionNoteCache, ExecutionTaggingIndexCache, HashedValuesCache, Oracle, PrivateExecutionOracle, UtilityExecutionOracle, executePrivateFunction, generateSimulatedProvingResult } from '@aztec/pxe/simulator';
9
9
  import { ExecutionError, WASMSimulator, createSimulationError, extractCallStack, resolveAssertionMessageFromError, toACVMWitness, witnessMapToFields } from '@aztec/simulator/client';
10
10
  import { CppPublicTxSimulator, GuardedMerkleTreeOperations, PublicContractsDB, PublicProcessor } from '@aztec/simulator/server';
@@ -13,17 +13,18 @@ import { AuthWitness } from '@aztec/stdlib/auth-witness';
13
13
  import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
14
14
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
15
15
  import { computePartialAddress } from '@aztec/stdlib/contract';
16
- import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
16
+ import { Gas, GasFees } 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
+ import { hashPublicKey } from '@aztec/stdlib/keys';
19
20
  import { ChonkProof } from '@aztec/stdlib/proofs';
20
21
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
21
22
  import { MerkleTreeId } from '@aztec/stdlib/trees';
22
23
  import { CallContext, HashedValues, PrivateExecutionResult, Tx, TxConstantData, TxContext, TxEffect, TxHash, collectNested } from '@aztec/stdlib/tx';
23
- import { ForkCheckpoint } from '@aztec/world-state';
24
- import { DEFAULT_ADDRESS } from '../constants.js';
25
- import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
24
+ import { ForkCheckpoint } from '@aztec/world-state/native';
25
+ import { DEFAULT_ADDRESS, MAX_PRIVATE_EVENTS_PER_TXE_QUERY, MAX_PRIVATE_EVENT_LEN } from '../constants.js';
26
26
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
27
+ import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js';
27
28
  export class TXEOracleTopLevelContext {
28
29
  stateMachine;
29
30
  contractStore;
@@ -40,10 +41,13 @@ export class TXEOracleTopLevelContext {
40
41
  version;
41
42
  chainId;
42
43
  authwits;
44
+ artifactResolver;
45
+ rootPath;
46
+ packageName;
43
47
  isMisc;
44
48
  isTxe;
45
49
  logger;
46
- constructor(stateMachine, contractStore, noteStore, keyStore, addressStore, accountStore, senderTaggingStore, recipientTaggingStore, senderAddressBookStore, capsuleStore, privateEventStore, nextBlockTimestamp, version, chainId, authwits){
50
+ constructor(stateMachine, contractStore, noteStore, keyStore, addressStore, accountStore, senderTaggingStore, recipientTaggingStore, senderAddressBookStore, capsuleStore, privateEventStore, nextBlockTimestamp, version, chainId, authwits, artifactResolver, rootPath, packageName){
47
51
  this.stateMachine = stateMachine;
48
52
  this.contractStore = contractStore;
49
53
  this.noteStore = noteStore;
@@ -59,15 +63,28 @@ export class TXEOracleTopLevelContext {
59
63
  this.version = version;
60
64
  this.chainId = chainId;
61
65
  this.authwits = authwits;
66
+ this.artifactResolver = artifactResolver;
67
+ this.rootPath = rootPath;
68
+ this.packageName = packageName;
62
69
  this.isMisc = true;
63
70
  this.isTxe = true;
64
71
  this.logger = createLogger('txe:top_level_context');
65
72
  this.logger.debug('Entering Top Level Context');
66
73
  }
67
- assertCompatibleOracleVersion(version) {
68
- if (version !== ORACLE_VERSION) {
69
- throw new Error(`Incompatible oracle version. TXE is using version '${ORACLE_VERSION}', but got a request for '${version}'.`);
74
+ contractOracleVersion;
75
+ assertCompatibleOracleVersion(major, minor) {
76
+ if (major !== ORACLE_VERSION_MAJOR) {
77
+ 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.';
78
+ throw new Error(`Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`);
70
79
  }
80
+ this.contractOracleVersion = {
81
+ major,
82
+ minor
83
+ };
84
+ }
85
+ // Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
86
+ nonOracleFunctionGetContractOracleVersion() {
87
+ return this.contractOracleVersion;
71
88
  }
72
89
  // This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
73
90
  // setup.
@@ -75,7 +92,7 @@ export class TXEOracleTopLevelContext {
75
92
  return Fr.random();
76
93
  }
77
94
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
78
- log(level, message, fields) {
95
+ log(level, message, _fieldsSize, fields) {
79
96
  if (!LogLevels[level]) {
80
97
  throw new Error(`Invalid log level: ${level}`);
81
98
  }
@@ -95,20 +112,27 @@ export class TXEOracleTopLevelContext {
95
112
  return Promise.resolve(this.nextBlockTimestamp);
96
113
  }
97
114
  async getLastBlockTimestamp() {
98
- return (await this.stateMachine.node.getBlockHeader('latest')).globalVariables.timestamp;
115
+ return (await this.stateMachine.node.getBlockData('latest')).header.globalVariables.timestamp;
99
116
  }
100
117
  async getLastTxEffects() {
101
118
  const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
102
- const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
119
+ const block = await this.stateMachine.archiver.getBlock({
120
+ number: latestBlockNumber
121
+ });
103
122
  if (block.body.txEffects.length != 1) {
104
123
  // Note that calls like env.mine() will result in blocks with no transactions, hitting this
105
124
  throw new Error(`Expected a single transaction in the last block, found ${block.body.txEffects.length}`);
106
125
  }
107
126
  const txEffects = block.body.txEffects[0];
127
+ const privateLogs = txEffects.privateLogs;
128
+ if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
129
+ throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
130
+ }
108
131
  return {
109
132
  txHash: txEffects.txHash,
110
133
  noteHashes: txEffects.noteHashes,
111
- nullifiers: txEffects.nullifiers
134
+ nullifiers: txEffects.nullifiers,
135
+ privateLogs
112
136
  };
113
137
  }
114
138
  async syncContractNonOracleMethod(contractAddress, scope, jobId) {
@@ -124,7 +148,7 @@ export class TXEOracleTopLevelContext {
124
148
  ]);
125
149
  }
126
150
  async getPrivateEvents(selector, contractAddress, scope) {
127
- return (await this.privateEventStore.getPrivateEvents(selector, {
151
+ const events = (await this.privateEventStore.getPrivateEvents(selector, {
128
152
  contractAddress,
129
153
  scopes: [
130
154
  scope
@@ -132,6 +156,13 @@ export class TXEOracleTopLevelContext {
132
156
  fromBlock: 0,
133
157
  toBlock: await this.getLastBlockNumber() + 1
134
158
  })).map((e)=>e.packedEvent);
159
+ if (events.length > MAX_PRIVATE_EVENTS_PER_TXE_QUERY) {
160
+ throw new Error(`Array of length ${events.length} larger than maxLen ${MAX_PRIVATE_EVENTS_PER_TXE_QUERY}`);
161
+ }
162
+ if (events.some((e)=>e.length > MAX_PRIVATE_EVENT_LEN)) {
163
+ throw new Error(`Some private event has length larger than maxLen ${MAX_PRIVATE_EVENT_LEN}`);
164
+ }
165
+ return events;
135
166
  }
136
167
  async advanceBlocksBy(blocks) {
137
168
  this.logger.debug(`time traveling ${blocks} blocks`);
@@ -143,22 +174,54 @@ export class TXEOracleTopLevelContext {
143
174
  this.logger.debug(`time traveling ${duration} seconds`);
144
175
  this.nextBlockTimestamp += duration;
145
176
  }
146
- async deploy(artifact, instance, secret) {
177
+ deploymentNullifier(address) {
178
+ return siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address.toField());
179
+ }
180
+ async deploy(contractPath, initializer, args, secret, salt, deployer) {
181
+ const { artifact, instance } = await this.artifactResolver.resolveDeployArtifact({
182
+ rootPath: this.rootPath,
183
+ packageName: this.packageName,
184
+ contractPath,
185
+ initializer,
186
+ args,
187
+ secret,
188
+ salt,
189
+ deployer
190
+ });
147
191
  // Emit deployment nullifier
148
192
  await this.mineBlock({
149
193
  nullifiers: [
150
- await siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), instance.address.toField())
194
+ await this.deploymentNullifier(instance.address)
151
195
  ]
152
196
  });
153
197
  if (!secret.equals(Fr.ZERO)) {
154
- await this.addAccount(artifact, instance, secret);
198
+ await this.registerContractAndAddAccount(artifact, instance, secret);
155
199
  } else {
156
200
  await this.contractStore.addContractInstance(instance);
157
201
  await this.contractStore.addContractArtifact(artifact);
158
202
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
159
203
  }
204
+ return [
205
+ instance.salt,
206
+ instance.deployer.toField(),
207
+ instance.currentContractClassId,
208
+ instance.initializationHash,
209
+ instance.immutablesHash,
210
+ ...instance.publicKeys.toFields()
211
+ ];
160
212
  }
161
- async addAccount(artifact, instance, secret) {
213
+ /**
214
+ * Mines a single block containing only the deployment nullifiers for the contracts at the given addresses.
215
+ */ async mineDeploymentNullifiers(addresses) {
216
+ await this.mineBlock({
217
+ nullifiers: await Promise.all(addresses.map((address)=>this.deploymentNullifier(address)))
218
+ });
219
+ }
220
+ async addAccount(secret) {
221
+ const { artifact, instance } = await this.artifactResolver.resolveAccountArtifact(secret);
222
+ return this.registerContractAndAddAccount(artifact, instance, secret);
223
+ }
224
+ async registerContractAndAddAccount(artifact, instance, secret) {
162
225
  const partialAddress = await computePartialAddress(instance);
163
226
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
164
227
  await this.contractStore.addContractInstance(instance);
@@ -179,12 +242,11 @@ export class TXEOracleTopLevelContext {
179
242
  }
180
243
  async addAuthWitness(address, messageHash) {
181
244
  const account = await this.accountStore.getAccount(address);
182
- const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
245
+ const ivpkMHash = await hashPublicKey(account.publicKeys.ivpkM);
246
+ const privateKey = await this.keyStore.getMasterSecretKey(ivpkMHash);
183
247
  const schnorr = new Schnorr();
184
- const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
185
- const authWitness = new AuthWitness(messageHash, [
186
- ...signature.toBuffer()
187
- ]);
248
+ const signature = await schnorr.constructSignature(messageHash, privateKey);
249
+ const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
188
250
  this.authwits.set(authWitness.requestHash.toString(), authWitness);
189
251
  }
190
252
  async mineBlock(options = {}) {
@@ -210,29 +272,26 @@ export class TXEOracleTopLevelContext {
210
272
  this.logger.info(`Created block ${blockNumber} with timestamp ${block.header.globalVariables.timestamp}`);
211
273
  await this.stateMachine.handleL2Block(block);
212
274
  }
213
- async privateCallNewFlow(from, targetContractAddress = AztecAddress.zero(), functionSelector = FunctionSelector.empty(), args, argsHash = Fr.zero(), isStaticCall = false, jobId) {
275
+ async privateCallNewFlow(from, targetContractAddress = AztecAddress.zero(), functionSelector = FunctionSelector.empty(), args, argsHash = Fr.zero(), isStaticCall = false, additionalScopes = [], jobId, authorizedUtilityCallTargets, gasSettings) {
214
276
  this.logger.verbose(`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`);
215
277
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
216
278
  if (!artifact) {
217
279
  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';
218
280
  throw new Error(message);
219
281
  }
220
- // When `from` is the zero address (e.g. when deploying a new account contract), we return an
221
- // empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
222
- const effectiveScopes = from.isZero() ? [] : [
223
- from
282
+ const scopes = from === undefined ? additionalScopes : [
283
+ from,
284
+ ...additionalScopes
224
285
  ];
225
286
  // Sync notes before executing private function to discover notes from previous transactions
226
287
  const utilityExecutor = async (call, execScopes)=>{
227
288
  await this.executeUtilityCall(call, execScopes, jobId);
228
289
  };
229
290
  const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
230
- await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, utilityExecutor, blockHeader, jobId, effectiveScopes);
291
+ await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, utilityExecutor, blockHeader, jobId, scopes);
231
292
  const blockNumber = await this.getNextBlockNumber();
232
- const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
233
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
234
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
235
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
293
+ const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
294
+ const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
236
295
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
237
296
  const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
238
297
  const noteCache = new ExecutionNoteCache(protocolNullifier);
@@ -264,18 +323,22 @@ export class TXEOracleTopLevelContext {
264
323
  senderTaggingStore: this.senderTaggingStore,
265
324
  recipientTaggingStore: this.recipientTaggingStore,
266
325
  senderAddressBookStore: this.senderAddressBookStore,
267
- capsuleStore: this.capsuleStore,
326
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
268
327
  privateEventStore: this.privateEventStore,
269
328
  contractSyncService: this.stateMachine.contractSyncService,
270
329
  jobId,
271
330
  totalPublicCalldataCount: 0,
272
331
  sideEffectCounter: minRevertibleSideEffectCounter,
273
- scopes: effectiveScopes,
332
+ scopes,
274
333
  // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
275
334
  // contract would perform, including setting senderForTags.
276
335
  senderForTags: from,
277
336
  simulator,
278
- messageContextService: this.stateMachine.messageContextService
337
+ messageContextService: this.stateMachine.messageContextService,
338
+ l2TipsStore: this.stateMachine.l2TipsProvider,
339
+ hooks: composeHooks({
340
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook(isStaticCall ? 'private view' : 'private', authorizedUtilityCallTargets)
341
+ })
279
342
  });
280
343
  // 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.
281
344
  let result;
@@ -288,7 +351,7 @@ export class TXEOracleTopLevelContext {
288
351
  r.publicInputs.publicTeardownCallRequest
289
352
  ]));
290
353
  const publicFunctionsCalldata = await Promise.all(publicCallRequests.map(async (r)=>{
291
- const calldata = await privateExecutionOracle.loadFromExecutionCache(r.calldataHash);
354
+ const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
292
355
  return new HashedValues(calldata, r.calldataHash);
293
356
  }));
294
357
  noteCache.finish();
@@ -347,10 +410,20 @@ export class TXEOracleTopLevelContext {
347
410
  throw new Error('Contract execution has reverted');
348
411
  }
349
412
  }
413
+ // Walk the nested private-call tree and collect every offchain effect the transaction emitted.
414
+ // PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
415
+ // `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
416
+ // we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
417
+ const offchainEffects = collectNested([
418
+ executionResult
419
+ ], (r)=>r.offchainEffects.map((e)=>e.data));
350
420
  if (isStaticCall) {
351
421
  await checkpoint.revert();
352
422
  await forkedWorldTrees.close();
353
- return executionResult.returnValues ?? [];
423
+ return {
424
+ returnValues: executionResult.returnValues ?? [],
425
+ offchainEffects
426
+ };
354
427
  }
355
428
  const txEffect = TxEffect.empty();
356
429
  txEffect.noteHashes = processedTx.txEffect.noteHashes;
@@ -366,14 +439,14 @@ export class TXEOracleTopLevelContext {
366
439
  ]);
367
440
  await this.stateMachine.handleL2Block(l2Block);
368
441
  await forkedWorldTrees.close();
369
- return executionResult.returnValues ?? [];
442
+ return {
443
+ returnValues: executionResult.returnValues ?? [],
444
+ offchainEffects
445
+ };
370
446
  }
371
- async publicCallNewFlow(from, targetContractAddress, calldata, isStaticCall) {
447
+ async publicCallNewFlow(from, targetContractAddress, calldata, isStaticCall, gasSettings) {
372
448
  this.logger.verbose(`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`);
373
449
  const blockNumber = await this.getNextBlockNumber();
374
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
375
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
376
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
377
450
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
378
451
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
379
452
  const calldataHash = await computeCalldataHash(calldata);
@@ -406,7 +479,7 @@ export class TXEOracleTopLevelContext {
406
479
  // The enqueued public call itself we make be revertible so that the public execution is itself revertible, as tests
407
480
  // may require producing reverts.
408
481
  const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
409
- revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(from, targetContractAddress, isStaticCall, calldataHash);
482
+ revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(from ?? AztecAddress.NULL_MSG_SENDER, targetContractAddress, isStaticCall, calldataHash);
410
483
  const inputsForPublic = new PartialPrivateTailPublicInputsForPublic(nonRevertibleAccumulatedData, revertibleAccumulatedData, PublicCallRequest.empty());
411
484
  const constantData = new TxConstantData(anchorBlockHeader, txContext, Fr.zero(), Fr.zero());
412
485
  const txData = new PrivateKernelTailCircuitPublicInputs(constantData, /*gasUsed=*/ new Gas(0, 0), /*feePayer=*/ AztecAddress.zero(), /*expirationTimestamp=*/ 0n, inputsForPublic, undefined);
@@ -462,7 +535,7 @@ export class TXEOracleTopLevelContext {
462
535
  await forkedWorldTrees.close();
463
536
  return returnValues ?? [];
464
537
  }
465
- async executeUtilityFunction(targetContractAddress, functionSelector, args, jobId) {
538
+ async executeUtilityFunction(targetContractAddress, functionSelector, args, jobId, authorizedUtilityCallTargets) {
466
539
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
467
540
  if (!artifact) {
468
541
  throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
@@ -471,7 +544,7 @@ export class TXEOracleTopLevelContext {
471
544
  const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
472
545
  await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, async (call, execScopes)=>{
473
546
  await this.executeUtilityCall(call, execScopes, jobId);
474
- }, blockHeader, jobId, 'ALL_SCOPES');
547
+ }, blockHeader, jobId, await this.keyStore.getAccounts());
475
548
  const call = FunctionCall.from({
476
549
  name: artifact.name,
477
550
  to: targetContractAddress,
@@ -482,9 +555,9 @@ export class TXEOracleTopLevelContext {
482
555
  args,
483
556
  returnTypes: []
484
557
  });
485
- return this.executeUtilityCall(call, 'ALL_SCOPES', jobId);
558
+ return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
486
559
  }
487
- async executeUtilityCall(call, scopes, jobId) {
560
+ async executeUtilityCall(call, scopes, jobId, authorizedUtilityCallTargets = []) {
488
561
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
489
562
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
490
563
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -495,6 +568,10 @@ export class TXEOracleTopLevelContext {
495
568
  });
496
569
  try {
497
570
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
571
+ const simulator = new WASMSimulator();
572
+ const utilityExecutor = async (syncCall, execScopes)=>{
573
+ await this.executeUtilityCall(syncCall, execScopes, jobId);
574
+ };
498
575
  const oracle = new UtilityExecutionOracle({
499
576
  contractAddress: call.to,
500
577
  authWitnesses: [],
@@ -507,13 +584,20 @@ export class TXEOracleTopLevelContext {
507
584
  aztecNode: this.stateMachine.node,
508
585
  recipientTaggingStore: this.recipientTaggingStore,
509
586
  senderAddressBookStore: this.senderAddressBookStore,
510
- capsuleStore: this.capsuleStore,
587
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
511
588
  privateEventStore: this.privateEventStore,
512
589
  messageContextService: this.stateMachine.messageContextService,
590
+ contractSyncService: this.stateMachine.contractSyncService,
591
+ l2TipsStore: this.stateMachine.l2TipsProvider,
513
592
  jobId,
514
- scopes
593
+ scopes,
594
+ simulator,
595
+ hooks: composeHooks({
596
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets)
597
+ }),
598
+ utilityExecutor
515
599
  });
516
- const acirExecutionResult = await new WASMSimulator().executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback()).catch((err)=>{
600
+ const acirExecutionResult = await simulator.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback()).catch((err)=>{
517
601
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
518
602
  throw new ExecutionError(err.message, {
519
603
  contractAddress: call.to,
@@ -536,7 +620,15 @@ export class TXEOracleTopLevelContext {
536
620
  ];
537
621
  }
538
622
  async getLastBlockNumber() {
539
- const header = await this.stateMachine.node.getBlockHeader('latest');
540
- return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
623
+ const block = await this.stateMachine.node.getBlock('latest');
624
+ return block ? block.header.globalVariables.blockNumber : BlockNumber.ZERO;
625
+ }
626
+ buildAuthorizeUtilityCallHook(callerContext, authorizedTargets) {
627
+ if (authorizedTargets.length === 0) {
628
+ return undefined;
629
+ }
630
+ return (req)=>Promise.resolve({
631
+ authorized: req.callerContext === callerContext && authorizedTargets.some((t)=>t.equals(req.target))
632
+ });
541
633
  }
542
634
  }
@@ -0,0 +1,17 @@
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 declare const TXE_ORACLE_VERSION_MAJOR = 1;
9
+ export declare const TXE_ORACLE_VERSION_MINOR = 2;
10
+ /**
11
+ * This hash is computed from the TXE oracle interfaces (IAvmExecutionOracle and ITxeExecutionOracle) and is used to
12
+ * detect when those interfaces change. When it does, bump:
13
+ * - TXE_ORACLE_VERSION_MAJOR (and reset MINOR to 0) for breaking changes, or
14
+ * - TXE_ORACLE_VERSION_MINOR for additive changes (new oracle method added).
15
+ */
16
+ export declare const TXE_ORACLE_INTERFACE_HASH = "9e5f6ad5fd170d1de5ddd417f19cce47b17382567e08360dc6a783154828e218";
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhlX29yYWNsZV92ZXJzaW9uLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3JhY2xlL3R4ZV9vcmFjbGVfdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFDSCxlQUFPLE1BQU0sd0JBQXdCLElBQUksQ0FBQztBQUMxQyxlQUFPLE1BQU0sd0JBQXdCLElBQUksQ0FBQztBQUUxQzs7Ozs7R0FLRztBQUNILGVBQU8sTUFBTSx5QkFBeUIscUVBQXFFLENBQUMifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"txe_oracle_version.d.ts","sourceRoot":"","sources":["../../src/oracle/txe_oracle_version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,qEAAqE,CAAC"}
@@ -0,0 +1,14 @@
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
+ */ export const TXE_ORACLE_VERSION_MAJOR = 1;
8
+ export const TXE_ORACLE_VERSION_MINOR = 2;
9
+ /**
10
+ * This hash is computed from the TXE oracle interfaces (IAvmExecutionOracle and ITxeExecutionOracle) and is used to
11
+ * detect when those interfaces change. When it does, bump:
12
+ * - TXE_ORACLE_VERSION_MAJOR (and reset MINOR to 0) for breaking changes, or
13
+ * - TXE_ORACLE_VERSION_MINOR for additive changes (new oracle method added).
14
+ */ export const TXE_ORACLE_INTERFACE_HASH = '9e5f6ad5fd170d1de5ddd417f19cce47b17382567e08360dc6a783154828e218';
@@ -0,0 +1,17 @@
1
+ import type { Fr } from '@aztec/foundation/curves/bn254';
2
+ import { PrivateExecutionOracle } from '@aztec/pxe/simulator';
3
+ import type { FunctionSelector } from '@aztec/stdlib/abi';
4
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
5
+ /**
6
+ * TXE-specific subclass of PrivateExecutionOracle that forbids operations not supported in
7
+ * TestEnvironment::private_context. TXE uses dedicated oracle flows (e.g. private_call) instead.
8
+ */
9
+ export declare class TXEPrivateExecutionOracle extends PrivateExecutionOracle {
10
+ callPrivateFunction(_targetContractAddress: AztecAddress, _functionSelector: FunctionSelector, _argsHash: Fr, _sideEffectCounter: number, _isStaticCall: boolean): Promise<{
11
+ endSideEffectCounter: Fr;
12
+ returnsHash: Fr;
13
+ }>;
14
+ assertValidPublicCalldata(_calldataHash: Fr): Promise<void>;
15
+ notifyRevertiblePhaseStart(_minRevertibleSideEffectCounter: number): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhlX3ByaXZhdGVfZXhlY3V0aW9uX29yYWNsZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL29yYWNsZS90eGVfcHJpdmF0ZV9leGVjdXRpb25fb3JhY2xlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3pELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzlELE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDMUQsT0FBTyxLQUFLLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFaEU7OztHQUdHO0FBQ0gscUJBQWEseUJBQTBCLFNBQVEsc0JBQXNCO0lBQzFELG1CQUFtQixDQUMxQixzQkFBc0IsRUFBRSxZQUFZLEVBQ3BDLGlCQUFpQixFQUFFLGdCQUFnQixFQUNuQyxTQUFTLEVBQUUsRUFBRSxFQUNiLGtCQUFrQixFQUFFLE1BQU0sRUFDMUIsYUFBYSxFQUFFLE9BQU8sR0FDckIsT0FBTyxDQUFDO1FBQUUsb0JBQW9CLEVBQUUsRUFBRSxDQUFDO1FBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQTtLQUFFLENBQUMsQ0FJeEQ7SUFFUSx5QkFBeUIsQ0FBQyxhQUFhLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFbkU7SUFFUSwwQkFBMEIsQ0FBQywrQkFBK0IsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUUxRjtDQUNGIn0=
@@ -0,0 +1 @@
1
+ {"version":3,"file":"txe_private_execution_oracle.d.ts","sourceRoot":"","sources":["../../src/oracle/txe_private_execution_oracle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,sBAAsB;IAC1D,mBAAmB,CAC1B,sBAAsB,EAAE,YAAY,EACpC,iBAAiB,EAAE,gBAAgB,EACnC,SAAS,EAAE,EAAE,EACb,kBAAkB,EAAE,MAAM,EAC1B,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC;QAAE,oBAAoB,EAAE,EAAE,CAAC;QAAC,WAAW,EAAE,EAAE,CAAA;KAAE,CAAC,CAIxD;IAEQ,yBAAyB,CAAC,aAAa,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnE;IAEQ,0BAA0B,CAAC,+BAA+B,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F;CACF"}
@@ -0,0 +1,15 @@
1
+ import { PrivateExecutionOracle } from '@aztec/pxe/simulator';
2
+ /**
3
+ * TXE-specific subclass of PrivateExecutionOracle that forbids operations not supported in
4
+ * TestEnvironment::private_context. TXE uses dedicated oracle flows (e.g. private_call) instead.
5
+ */ export class TXEPrivateExecutionOracle extends PrivateExecutionOracle {
6
+ callPrivateFunction(_targetContractAddress, _functionSelector, _argsHash, _sideEffectCounter, _isStaticCall) {
7
+ throw new Error('Contract calls are forbidden inside a `TestEnvironment::private_context`, use `private_call` instead');
8
+ }
9
+ assertValidPublicCalldata(_calldataHash) {
10
+ throw new Error('Enqueueing public calls is not supported in TestEnvironment::private_context');
11
+ }
12
+ notifyRevertiblePhaseStart(_minRevertibleSideEffectCounter) {
13
+ throw new Error('Enqueueing public calls is not supported in TestEnvironment::private_context');
14
+ }
15
+ }
@@ -0,0 +1,14 @@
1
+ import type { Logger } from '@aztec/foundation/log';
2
+ /**
3
+ * Creates the TXE RPC server. With `TXE_WORKERS=1` oracle calls run on the main thread (no
4
+ * worker_threads, no IPC overhead). With any other value oracle calls are
5
+ * routed to a pool of worker threads sized to that value, sticky by `session_id`.
6
+ *
7
+ * Each incoming TCP socket is tagged with the `session_id` of the first oracle call it carries —
8
+ * nargo uses one HTTP client per test, so the socket-to-session mapping is 1:1. When the socket
9
+ * closes (end of test), the dispatcher disposes the session and frees its world state + LMDB.
10
+ *
11
+ * Lives in its own module so the worker bundle does not pull in the HTTP server stack.
12
+ */
13
+ export declare function createTXERpcServer(logger: Logger): Promise<import("@aztec/foundation/json-rpc/server").SafeJsonRpcServer>;
14
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnBjX3NlcnZlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3JwY19zZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFlcEQ7Ozs7Ozs7Ozs7R0FVRztBQUNILHdCQUFzQixrQkFBa0IsQ0FBQyxNQUFNLEVBQUUsTUFBTSwwRUE0Q3REIn0=
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc_server.d.ts","sourceRoot":"","sources":["../src/rpc_server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAepD;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,0EA4CtD"}
@@ -0,0 +1,78 @@
1
+ import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
2
+ import { TXEDispatcherPool, buildSharedContractStore } from './dispatcher_pool.js';
3
+ import { TXEDispatcher, TXEDispatcherApiSchema } from './index.js';
4
+ /**
5
+ * Symbol used to tag an incoming TCP socket with the `session_id` it has been associated with.
6
+ * Hidden under a Symbol so we don't risk colliding with anything koa or http core adds.
7
+ */ const SESSION_SYMBOL = Symbol('txeSessionId');
8
+ /**
9
+ * Creates the TXE RPC server. With `TXE_WORKERS=1` oracle calls run on the main thread (no
10
+ * worker_threads, no IPC overhead). With any other value oracle calls are
11
+ * routed to a pool of worker threads sized to that value, sticky by `session_id`.
12
+ *
13
+ * Each incoming TCP socket is tagged with the `session_id` of the first oracle call it carries —
14
+ * nargo uses one HTTP client per test, so the socket-to-session mapping is 1:1. When the socket
15
+ * closes (end of test), the dispatcher disposes the session and frees its world state + LMDB.
16
+ *
17
+ * Lives in its own module so the worker bundle does not pull in the HTTP server stack.
18
+ */ export async function createTXERpcServer(logger) {
19
+ const workerCount = Number(process.env.TXE_WORKERS);
20
+ let dispatcher;
21
+ if (workerCount === 1) {
22
+ const { dataDir, schnorrClassId } = await buildSharedContractStore();
23
+ dispatcher = new TXEDispatcher(logger, {
24
+ contractStoreSourceDir: dataDir,
25
+ schnorrClassId
26
+ });
27
+ } else {
28
+ dispatcher = new TXEDispatcherPool(logger, {
29
+ workers: Number.isFinite(workerCount) && workerCount > 1 ? workerCount : undefined
30
+ });
31
+ }
32
+ const server = createSafeJsonRpcServer(dispatcher, TXEDispatcherApiSchema, {
33
+ http200OnError: true,
34
+ middlewares: [
35
+ async (ctx, next)=>{
36
+ // The body parser runs further down the chain, so `ctx.request.body` is populated only
37
+ // after `next()` resolves.
38
+ await next();
39
+ const socket = ctx.req.socket;
40
+ if (socket[SESSION_SYMBOL] !== undefined) {
41
+ return;
42
+ }
43
+ const body = ctx.request.body;
44
+ const sessionId = body && typeof body === 'object' && 'params' in body ? extractSessionId(body.params) : undefined;
45
+ if (sessionId === undefined) {
46
+ return;
47
+ }
48
+ socket[SESSION_SYMBOL] = sessionId;
49
+ logger.debug(`Tagged socket with session`, {
50
+ sessionId,
51
+ remoteAddress: socket.remoteAddress,
52
+ remotePort: socket.remotePort
53
+ });
54
+ socket.once('close', ()=>{
55
+ logger.debug(`Disposing session on socket close`, {
56
+ sessionId
57
+ });
58
+ void dispatcher.disposeSession(sessionId);
59
+ });
60
+ }
61
+ ]
62
+ });
63
+ return server;
64
+ }
65
+ // Extracts `session_id` from a JSON-RPC `params` array. Always a `number` because session_id
66
+ // comes off `JSON.parse`, which never produces BigInt; values above MAX_SAFE_INTEGER lose
67
+ // precision but still work as a Map key.
68
+ function extractSessionId(params) {
69
+ if (!Array.isArray(params) || params.length === 0) {
70
+ return undefined;
71
+ }
72
+ const first = params[0];
73
+ if (first && typeof first === 'object' && 'session_id' in first) {
74
+ const sid = first.session_id;
75
+ return typeof sid === 'number' ? sid : undefined;
76
+ }
77
+ return undefined;
78
+ }