@aztec/txe 0.0.1-commit.7b97ef96e → 0.0.1-commit.7cbc774

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 (126) hide show
  1. package/dest/ContractClassRegistry-KKNT527R.js +3 -0
  2. package/dest/ContractClassRegistry-KKNT527R.js.map +7 -0
  3. package/dest/ContractInstanceRegistry-GSKBNL3Q.js +3 -0
  4. package/dest/ContractInstanceRegistry-GSKBNL3Q.js.map +7 -0
  5. package/dest/FeeJuice-GUJ5O6GB.js +3 -0
  6. package/dest/FeeJuice-GUJ5O6GB.js.map +7 -0
  7. package/dest/MultiCallEntrypoint-ZQPCKVKL.js +3 -0
  8. package/dest/MultiCallEntrypoint-ZQPCKVKL.js.map +7 -0
  9. package/dest/SchnorrAccount-QIXE5DXA.js +3 -0
  10. package/dest/SchnorrAccount-QIXE5DXA.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 +32 -0
  14. package/dest/bin/index.js +3 -30
  15. package/dest/bin/index.js.map +7 -0
  16. package/dest/chunk-BJVAAXNA.js +3 -0
  17. package/dest/chunk-BJVAAXNA.js.map +7 -0
  18. package/dest/chunk-DF6RMOC6.js +304 -0
  19. package/dest/chunk-DF6RMOC6.js.map +7 -0
  20. package/dest/chunk-PPJTNNUG.js +265 -0
  21. package/dest/chunk-PPJTNNUG.js.map +7 -0
  22. package/dest/constants.d.ts +3 -1
  23. package/dest/constants.d.ts.map +1 -1
  24. package/dest/constants.js +5 -0
  25. package/dest/dispatcher_pool.d.ts +67 -0
  26. package/dest/dispatcher_pool.d.ts.map +1 -0
  27. package/dest/dispatcher_pool.js +284 -0
  28. package/dest/index.d.ts +188 -7
  29. package/dest/index.d.ts.map +1 -1
  30. package/dest/index.js +160 -119
  31. package/dest/metafile.json +38147 -0
  32. package/dest/msgpackr_fr_extension.d.ts +2 -0
  33. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  34. package/dest/msgpackr_fr_extension.js +21 -0
  35. package/dest/oracle/interfaces.d.ts +40 -28
  36. package/dest/oracle/interfaces.d.ts.map +1 -1
  37. package/dest/oracle/txe_oracle_public_context.d.ts +18 -13
  38. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  39. package/dest/oracle/txe_oracle_public_context.js +27 -12
  40. package/dest/oracle/txe_oracle_top_level_context.d.ts +38 -22
  41. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  42. package/dest/oracle/txe_oracle_top_level_context.js +141 -72
  43. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  44. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  45. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  46. package/dest/rpc_server.d.ts +14 -0
  47. package/dest/rpc_server.d.ts.map +1 -0
  48. package/dest/rpc_server.js +78 -0
  49. package/dest/rpc_translator.d.ts +139 -83
  50. package/dest/rpc_translator.d.ts.map +1 -1
  51. package/dest/rpc_translator.js +574 -238
  52. package/dest/server.bundle.js +3 -0
  53. package/dest/server.bundle.js.map +7 -0
  54. package/dest/state_machine/archiver.d.ts +4 -4
  55. package/dest/state_machine/archiver.d.ts.map +1 -1
  56. package/dest/state_machine/archiver.js +22 -20
  57. package/dest/state_machine/dummy_p2p_client.d.ts +14 -8
  58. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  59. package/dest/state_machine/dummy_p2p_client.js +17 -5
  60. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  61. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  62. package/dest/state_machine/global_variable_builder.js +9 -3
  63. package/dest/state_machine/index.d.ts +7 -3
  64. package/dest/state_machine/index.d.ts.map +1 -1
  65. package/dest/state_machine/index.js +23 -5
  66. package/dest/state_machine/mock_epoch_cache.d.ts +18 -3
  67. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  68. package/dest/state_machine/mock_epoch_cache.js +35 -2
  69. package/dest/state_machine/synchronizer.d.ts +5 -5
  70. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  71. package/dest/state_machine/synchronizer.js +4 -4
  72. package/dest/txe_oracle_version.d.ts +17 -0
  73. package/dest/txe_oracle_version.d.ts.map +1 -0
  74. package/dest/txe_oracle_version.js +14 -0
  75. package/dest/txe_session.d.ts +69 -6
  76. package/dest/txe_session.d.ts.map +1 -1
  77. package/dest/txe_session.js +170 -35
  78. package/dest/utils/encoding.d.ts +191 -0
  79. package/dest/utils/encoding.d.ts.map +1 -0
  80. package/dest/{util → utils}/encoding.js +7 -2
  81. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  82. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  83. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  84. package/dest/utils/txe_account_store.d.ts.map +1 -0
  85. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  86. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  87. package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
  88. package/dest/worker.bundle.js +3 -0
  89. package/dest/worker.bundle.js.map +7 -0
  90. package/dest/worker.d.ts +2 -0
  91. package/dest/worker.d.ts.map +1 -0
  92. package/dest/worker.js +92 -0
  93. package/package.json +37 -21
  94. package/src/bin/check_txe_oracle_version.ts +37 -0
  95. package/src/bin/index.ts +11 -2
  96. package/src/constants.ts +6 -0
  97. package/src/dispatcher_pool.ts +316 -0
  98. package/src/index.ts +178 -126
  99. package/src/msgpackr_fr_extension.ts +23 -0
  100. package/src/oracle/interfaces.ts +48 -34
  101. package/src/oracle/txe_oracle_public_context.ts +42 -12
  102. package/src/oracle/txe_oracle_top_level_context.ts +176 -83
  103. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  104. package/src/rpc_server.ts +87 -0
  105. package/src/rpc_translator.ts +654 -319
  106. package/src/state_machine/archiver.ts +33 -18
  107. package/src/state_machine/dummy_p2p_client.ts +32 -12
  108. package/src/state_machine/global_variable_builder.ts +18 -3
  109. package/src/state_machine/index.ts +25 -6
  110. package/src/state_machine/mock_epoch_cache.ts +46 -3
  111. package/src/state_machine/synchronizer.ts +5 -5
  112. package/src/txe_oracle_version.ts +17 -0
  113. package/src/txe_session.ts +269 -37
  114. package/src/{util → utils}/encoding.ts +8 -2
  115. package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
  116. package/src/worker.ts +98 -0
  117. package/dest/util/encoding.d.ts +0 -720
  118. package/dest/util/encoding.d.ts.map +0 -1
  119. package/dest/util/expected_failure_error.d.ts.map +0 -1
  120. package/dest/util/txe_account_store.d.ts.map +0 -1
  121. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  122. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  123. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  124. /package/dest/{util → utils}/txe_account_store.js +0 -0
  125. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  126. /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 { ForkCheckpoint } from '@aztec/world-state/native';
24
25
  import { DEFAULT_ADDRESS } from '../constants.js';
25
- import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.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;
@@ -36,7 +37,6 @@ export class TXEOracleTopLevelContext {
36
37
  senderAddressBookStore;
37
38
  capsuleStore;
38
39
  privateEventStore;
39
- jobId;
40
40
  nextBlockTimestamp;
41
41
  version;
42
42
  chainId;
@@ -44,7 +44,7 @@ export class TXEOracleTopLevelContext {
44
44
  isMisc;
45
45
  isTxe;
46
46
  logger;
47
- constructor(stateMachine, contractStore, noteStore, keyStore, addressStore, accountStore, senderTaggingStore, recipientTaggingStore, senderAddressBookStore, capsuleStore, privateEventStore, jobId, nextBlockTimestamp, version, chainId, authwits){
47
+ constructor(stateMachine, contractStore, noteStore, keyStore, addressStore, accountStore, senderTaggingStore, recipientTaggingStore, senderAddressBookStore, capsuleStore, privateEventStore, nextBlockTimestamp, version, chainId, authwits){
48
48
  this.stateMachine = stateMachine;
49
49
  this.contractStore = contractStore;
50
50
  this.noteStore = noteStore;
@@ -56,7 +56,6 @@ export class TXEOracleTopLevelContext {
56
56
  this.senderAddressBookStore = senderAddressBookStore;
57
57
  this.capsuleStore = capsuleStore;
58
58
  this.privateEventStore = privateEventStore;
59
- this.jobId = jobId;
60
59
  this.nextBlockTimestamp = nextBlockTimestamp;
61
60
  this.version = version;
62
61
  this.chainId = chainId;
@@ -66,18 +65,28 @@ export class TXEOracleTopLevelContext {
66
65
  this.logger = createLogger('txe:top_level_context');
67
66
  this.logger.debug('Entering Top Level Context');
68
67
  }
69
- utilityAssertCompatibleOracleVersion(version) {
70
- if (version !== ORACLE_VERSION) {
71
- throw new Error(`Incompatible oracle version. TXE is using version '${ORACLE_VERSION}', but got a request for '${version}'.`);
68
+ contractOracleVersion;
69
+ assertCompatibleOracleVersion(major, minor) {
70
+ if (major !== ORACLE_VERSION_MAJOR) {
71
+ 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.';
72
+ throw new Error(`Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`);
72
73
  }
74
+ this.contractOracleVersion = {
75
+ major,
76
+ minor
77
+ };
78
+ }
79
+ // Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
80
+ nonOracleFunctionGetContractOracleVersion() {
81
+ return this.contractOracleVersion;
73
82
  }
74
83
  // This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
75
84
  // setup.
76
- utilityGetRandomField() {
85
+ getRandomField() {
77
86
  return Fr.random();
78
87
  }
79
88
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
80
- utilityLog(level, message, fields) {
89
+ log(level, message, fields) {
81
90
  if (!LogLevels[level]) {
82
91
  throw new Error(`Invalid log level: ${level}`);
83
92
  }
@@ -87,33 +96,52 @@ export class TXEOracleTopLevelContext {
87
96
  });
88
97
  return Promise.resolve();
89
98
  }
90
- txeGetDefaultAddress() {
99
+ getDefaultAddress() {
91
100
  return DEFAULT_ADDRESS;
92
101
  }
93
- async txeGetNextBlockNumber() {
102
+ async getNextBlockNumber() {
94
103
  return BlockNumber(await this.getLastBlockNumber() + 1);
95
104
  }
96
- txeGetNextBlockTimestamp() {
105
+ getNextBlockTimestamp() {
97
106
  return Promise.resolve(this.nextBlockTimestamp);
98
107
  }
99
- async txeGetLastBlockTimestamp() {
100
- return (await this.stateMachine.node.getBlockHeader('latest')).globalVariables.timestamp;
108
+ async getLastBlockTimestamp() {
109
+ return (await this.stateMachine.node.getBlockData('latest')).header.globalVariables.timestamp;
101
110
  }
102
- async txeGetLastTxEffects() {
111
+ async getLastTxEffects() {
103
112
  const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
104
- const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
113
+ const block = await this.stateMachine.archiver.getBlock({
114
+ number: latestBlockNumber
115
+ });
105
116
  if (block.body.txEffects.length != 1) {
106
117
  // Note that calls like env.mine() will result in blocks with no transactions, hitting this
107
118
  throw new Error(`Expected a single transaction in the last block, found ${block.body.txEffects.length}`);
108
119
  }
109
120
  const txEffects = block.body.txEffects[0];
121
+ const privateLogs = txEffects.privateLogs;
122
+ if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
123
+ throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
124
+ }
110
125
  return {
111
126
  txHash: txEffects.txHash,
112
127
  noteHashes: txEffects.noteHashes,
113
- nullifiers: txEffects.nullifiers
128
+ nullifiers: txEffects.nullifiers,
129
+ privateLogs
114
130
  };
115
131
  }
116
- async txeGetPrivateEvents(selector, contractAddress, scope) {
132
+ async syncContractNonOracleMethod(contractAddress, scope, jobId) {
133
+ if (contractAddress.equals(DEFAULT_ADDRESS)) {
134
+ this.logger.debug(`Skipping sync in getPrivateEvents because the events correspond to the default address.`);
135
+ return;
136
+ }
137
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
138
+ await this.stateMachine.contractSyncService.ensureContractSynced(contractAddress, null, async (call, execScopes)=>{
139
+ await this.executeUtilityCall(call, execScopes, jobId);
140
+ }, blockHeader, jobId, [
141
+ scope
142
+ ]);
143
+ }
144
+ async getPrivateEvents(selector, contractAddress, scope) {
117
145
  return (await this.privateEventStore.getPrivateEvents(selector, {
118
146
  contractAddress,
119
147
  scopes: [
@@ -123,32 +151,42 @@ export class TXEOracleTopLevelContext {
123
151
  toBlock: await this.getLastBlockNumber() + 1
124
152
  })).map((e)=>e.packedEvent);
125
153
  }
126
- async txeAdvanceBlocksBy(blocks) {
154
+ async advanceBlocksBy(blocks) {
127
155
  this.logger.debug(`time traveling ${blocks} blocks`);
128
156
  for(let i = 0; i < blocks; i++){
129
157
  await this.mineBlock();
130
158
  }
131
159
  }
132
- txeAdvanceTimestampBy(duration) {
160
+ advanceTimestampBy(duration) {
133
161
  this.logger.debug(`time traveling ${duration} seconds`);
134
162
  this.nextBlockTimestamp += duration;
135
163
  }
136
- async txeDeploy(artifact, instance, secret) {
164
+ deploymentNullifier(address) {
165
+ return siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address.toField());
166
+ }
167
+ async deploy(artifact, instance, secret) {
137
168
  // Emit deployment nullifier
138
169
  await this.mineBlock({
139
170
  nullifiers: [
140
- await siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), instance.address.toField())
171
+ await this.deploymentNullifier(instance.address)
141
172
  ]
142
173
  });
143
174
  if (!secret.equals(Fr.ZERO)) {
144
- await this.txeAddAccount(artifact, instance, secret);
175
+ await this.addAccount(artifact, instance, secret);
145
176
  } else {
146
177
  await this.contractStore.addContractInstance(instance);
147
178
  await this.contractStore.addContractArtifact(artifact);
148
179
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
149
180
  }
150
181
  }
151
- async txeAddAccount(artifact, instance, secret) {
182
+ /**
183
+ * Mines a single block containing only the deployment nullifiers for the contracts at the given addresses.
184
+ */ async mineDeploymentNullifiers(addresses) {
185
+ await this.mineBlock({
186
+ nullifiers: await Promise.all(addresses.map((address)=>this.deploymentNullifier(address)))
187
+ });
188
+ }
189
+ async addAccount(artifact, instance, secret) {
152
190
  const partialAddress = await computePartialAddress(instance);
153
191
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
154
192
  await this.contractStore.addContractInstance(instance);
@@ -159,7 +197,7 @@ export class TXEOracleTopLevelContext {
159
197
  this.logger.debug(`Created account ${completeAddress.address}`);
160
198
  return completeAddress;
161
199
  }
162
- async txeCreateAccount(secret) {
200
+ async createAccount(secret) {
163
201
  // This is a foot gun !
164
202
  const completeAddress = await this.keyStore.addAccount(secret, secret);
165
203
  await this.accountStore.setAccount(completeAddress.address, completeAddress);
@@ -167,18 +205,17 @@ export class TXEOracleTopLevelContext {
167
205
  this.logger.debug(`Created account ${completeAddress.address}`);
168
206
  return completeAddress;
169
207
  }
170
- async txeAddAuthWitness(address, messageHash) {
208
+ async addAuthWitness(address, messageHash) {
171
209
  const account = await this.accountStore.getAccount(address);
172
- const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
210
+ const ivpkMHash = await hashPublicKey(account.publicKeys.ivpkM);
211
+ const privateKey = await this.keyStore.getMasterSecretKey(ivpkMHash);
173
212
  const schnorr = new Schnorr();
174
- const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
175
- const authWitness = new AuthWitness(messageHash, [
176
- ...signature.toBuffer()
177
- ]);
213
+ const signature = await schnorr.constructSignature(messageHash, privateKey);
214
+ const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
178
215
  this.authwits.set(authWitness.requestHash.toString(), authWitness);
179
216
  }
180
217
  async mineBlock(options = {}) {
181
- const blockNumber = await this.txeGetNextBlockNumber();
218
+ const blockNumber = await this.getNextBlockNumber();
182
219
  const txEffect = TxEffect.empty();
183
220
  txEffect.nullifiers = [
184
221
  getSingleTxBlockRequestHash(blockNumber),
@@ -200,29 +237,26 @@ export class TXEOracleTopLevelContext {
200
237
  this.logger.info(`Created block ${blockNumber} with timestamp ${block.header.globalVariables.timestamp}`);
201
238
  await this.stateMachine.handleL2Block(block);
202
239
  }
203
- async txePrivateCallNewFlow(from, targetContractAddress = AztecAddress.zero(), functionSelector = FunctionSelector.empty(), args, argsHash = Fr.zero(), isStaticCall = false) {
240
+ async privateCallNewFlow(from, targetContractAddress = AztecAddress.zero(), functionSelector = FunctionSelector.empty(), args, argsHash = Fr.zero(), isStaticCall = false, additionalScopes = [], jobId, authorizedUtilityCallTargets, gasSettings) {
204
241
  this.logger.verbose(`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`);
205
242
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
206
243
  if (!artifact) {
207
244
  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';
208
245
  throw new Error(message);
209
246
  }
210
- // When `from` is the zero address (e.g. when deploying a new account contract), we return an
211
- // empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
212
- const effectiveScopes = from.isZero() ? [] : [
213
- from
247
+ const scopes = from === undefined ? additionalScopes : [
248
+ from,
249
+ ...additionalScopes
214
250
  ];
215
251
  // Sync notes before executing private function to discover notes from previous transactions
216
252
  const utilityExecutor = async (call, execScopes)=>{
217
- await this.executeUtilityCall(call, execScopes);
253
+ await this.executeUtilityCall(call, execScopes, jobId);
218
254
  };
219
255
  const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
220
- await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, utilityExecutor, blockHeader, this.jobId, effectiveScopes);
221
- const blockNumber = await this.txeGetNextBlockNumber();
222
- const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
223
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
224
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
225
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
256
+ await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, utilityExecutor, blockHeader, jobId, scopes);
257
+ const blockNumber = await this.getNextBlockNumber();
258
+ const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
259
+ const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
226
260
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
227
261
  const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
228
262
  const noteCache = new ExecutionNoteCache(protocolNullifier);
@@ -254,17 +288,22 @@ export class TXEOracleTopLevelContext {
254
288
  senderTaggingStore: this.senderTaggingStore,
255
289
  recipientTaggingStore: this.recipientTaggingStore,
256
290
  senderAddressBookStore: this.senderAddressBookStore,
257
- capsuleStore: this.capsuleStore,
291
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
258
292
  privateEventStore: this.privateEventStore,
259
293
  contractSyncService: this.stateMachine.contractSyncService,
260
- jobId: this.jobId,
294
+ jobId,
261
295
  totalPublicCalldataCount: 0,
262
296
  sideEffectCounter: minRevertibleSideEffectCounter,
263
- scopes: effectiveScopes,
297
+ scopes,
264
298
  // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
265
299
  // contract would perform, including setting senderForTags.
266
300
  senderForTags: from,
267
- simulator
301
+ simulator,
302
+ messageContextService: this.stateMachine.messageContextService,
303
+ l2TipsStore: this.stateMachine.l2TipsProvider,
304
+ hooks: composeHooks({
305
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook(isStaticCall ? 'private view' : 'private', authorizedUtilityCallTargets)
306
+ })
268
307
  });
269
308
  // 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.
270
309
  let result;
@@ -277,7 +316,7 @@ export class TXEOracleTopLevelContext {
277
316
  r.publicInputs.publicTeardownCallRequest
278
317
  ]));
279
318
  const publicFunctionsCalldata = await Promise.all(publicCallRequests.map(async (r)=>{
280
- const calldata = await privateExecutionOracle.privateLoadFromExecutionCache(r.calldataHash);
319
+ const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
281
320
  return new HashedValues(calldata, r.calldataHash);
282
321
  }));
283
322
  noteCache.finish();
@@ -336,10 +375,20 @@ export class TXEOracleTopLevelContext {
336
375
  throw new Error('Contract execution has reverted');
337
376
  }
338
377
  }
378
+ // Walk the nested private-call tree and collect every offchain effect the transaction emitted.
379
+ // PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
380
+ // `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
381
+ // we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
382
+ const offchainEffects = collectNested([
383
+ executionResult
384
+ ], (r)=>r.offchainEffects.map((e)=>e.data));
339
385
  if (isStaticCall) {
340
386
  await checkpoint.revert();
341
387
  await forkedWorldTrees.close();
342
- return executionResult.returnValues ?? [];
388
+ return {
389
+ returnValues: executionResult.returnValues ?? [],
390
+ offchainEffects
391
+ };
343
392
  }
344
393
  const txEffect = TxEffect.empty();
345
394
  txEffect.noteHashes = processedTx.txEffect.noteHashes;
@@ -355,14 +404,14 @@ export class TXEOracleTopLevelContext {
355
404
  ]);
356
405
  await this.stateMachine.handleL2Block(l2Block);
357
406
  await forkedWorldTrees.close();
358
- return executionResult.returnValues ?? [];
407
+ return {
408
+ returnValues: executionResult.returnValues ?? [],
409
+ offchainEffects
410
+ };
359
411
  }
360
- async txePublicCallNewFlow(from, targetContractAddress, calldata, isStaticCall) {
412
+ async publicCallNewFlow(from, targetContractAddress, calldata, isStaticCall, gasSettings) {
361
413
  this.logger.verbose(`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`);
362
- const blockNumber = await this.txeGetNextBlockNumber();
363
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
364
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
365
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
414
+ const blockNumber = await this.getNextBlockNumber();
366
415
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
367
416
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
368
417
  const calldataHash = await computeCalldataHash(calldata);
@@ -395,7 +444,7 @@ export class TXEOracleTopLevelContext {
395
444
  // The enqueued public call itself we make be revertible so that the public execution is itself revertible, as tests
396
445
  // may require producing reverts.
397
446
  const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
398
- revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(from, targetContractAddress, isStaticCall, calldataHash);
447
+ revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(from ?? AztecAddress.NULL_MSG_SENDER, targetContractAddress, isStaticCall, calldataHash);
399
448
  const inputsForPublic = new PartialPrivateTailPublicInputsForPublic(nonRevertibleAccumulatedData, revertibleAccumulatedData, PublicCallRequest.empty());
400
449
  const constantData = new TxConstantData(anchorBlockHeader, txContext, Fr.zero(), Fr.zero());
401
450
  const txData = new PrivateKernelTailCircuitPublicInputs(constantData, /*gasUsed=*/ new Gas(0, 0), /*feePayer=*/ AztecAddress.zero(), /*expirationTimestamp=*/ 0n, inputsForPublic, undefined);
@@ -451,7 +500,7 @@ export class TXEOracleTopLevelContext {
451
500
  await forkedWorldTrees.close();
452
501
  return returnValues ?? [];
453
502
  }
454
- async txeExecuteUtilityFunction(targetContractAddress, functionSelector, args) {
503
+ async executeUtilityFunction(targetContractAddress, functionSelector, args, jobId, authorizedUtilityCallTargets) {
455
504
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
456
505
  if (!artifact) {
457
506
  throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
@@ -459,8 +508,8 @@ export class TXEOracleTopLevelContext {
459
508
  // Sync notes before executing utility function to discover notes from previous transactions
460
509
  const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
461
510
  await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, async (call, execScopes)=>{
462
- await this.executeUtilityCall(call, execScopes);
463
- }, blockHeader, this.jobId, 'ALL_SCOPES');
511
+ await this.executeUtilityCall(call, execScopes, jobId);
512
+ }, blockHeader, jobId, await this.keyStore.getAccounts());
464
513
  const call = FunctionCall.from({
465
514
  name: artifact.name,
466
515
  to: targetContractAddress,
@@ -471,9 +520,9 @@ export class TXEOracleTopLevelContext {
471
520
  args,
472
521
  returnTypes: []
473
522
  });
474
- return this.executeUtilityCall(call, 'ALL_SCOPES');
523
+ return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
475
524
  }
476
- async executeUtilityCall(call, scopes) {
525
+ async executeUtilityCall(call, scopes, jobId, authorizedUtilityCallTargets = []) {
477
526
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
478
527
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
479
528
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -484,6 +533,10 @@ export class TXEOracleTopLevelContext {
484
533
  });
485
534
  try {
486
535
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
536
+ const simulator = new WASMSimulator();
537
+ const utilityExecutor = async (syncCall, execScopes)=>{
538
+ await this.executeUtilityCall(syncCall, execScopes, jobId);
539
+ };
487
540
  const oracle = new UtilityExecutionOracle({
488
541
  contractAddress: call.to,
489
542
  authWitnesses: [],
@@ -496,12 +549,20 @@ export class TXEOracleTopLevelContext {
496
549
  aztecNode: this.stateMachine.node,
497
550
  recipientTaggingStore: this.recipientTaggingStore,
498
551
  senderAddressBookStore: this.senderAddressBookStore,
499
- capsuleStore: this.capsuleStore,
552
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
500
553
  privateEventStore: this.privateEventStore,
501
- jobId: this.jobId,
502
- scopes
554
+ messageContextService: this.stateMachine.messageContextService,
555
+ contractSyncService: this.stateMachine.contractSyncService,
556
+ l2TipsStore: this.stateMachine.l2TipsProvider,
557
+ jobId,
558
+ scopes,
559
+ simulator,
560
+ hooks: composeHooks({
561
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets)
562
+ }),
563
+ utilityExecutor
503
564
  });
504
- const acirExecutionResult = await new WASMSimulator().executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback()).catch((err)=>{
565
+ const acirExecutionResult = await simulator.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback()).catch((err)=>{
505
566
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
506
567
  throw new ExecutionError(err.message, {
507
568
  contractAddress: call.to,
@@ -524,7 +585,15 @@ export class TXEOracleTopLevelContext {
524
585
  ];
525
586
  }
526
587
  async getLastBlockNumber() {
527
- const header = await this.stateMachine.node.getBlockHeader('latest');
528
- return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
588
+ const block = await this.stateMachine.node.getBlock('latest');
589
+ return block ? block.header.globalVariables.blockNumber : BlockNumber.ZERO;
590
+ }
591
+ buildAuthorizeUtilityCallHook(callerContext, authorizedTargets) {
592
+ if (authorizedTargets.length === 0) {
593
+ return undefined;
594
+ }
595
+ return (req)=>Promise.resolve({
596
+ authorized: req.callerContext === callerContext && authorizedTargets.some((t)=>t.equals(req.target))
597
+ });
529
598
  }
530
599
  }
@@ -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
+ }