@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.
- package/dest/ContractClassRegistry-PII7Q556.js +3 -0
- package/dest/ContractClassRegistry-PII7Q556.js.map +7 -0
- package/dest/ContractInstanceRegistry-3MHLGFNZ.js +3 -0
- package/dest/ContractInstanceRegistry-3MHLGFNZ.js.map +7 -0
- package/dest/FeeJuice-TQV5AQK7.js +3 -0
- package/dest/FeeJuice-TQV5AQK7.js.map +7 -0
- package/dest/MultiCallEntrypoint-SBJUIJY7.js +3 -0
- package/dest/MultiCallEntrypoint-SBJUIJY7.js.map +7 -0
- package/dest/SchnorrAccount-MQXPIZ44.js +3 -0
- package/dest/SchnorrAccount-MQXPIZ44.js.map +7 -0
- package/dest/bin/check_txe_oracle_version.d.ts +2 -0
- package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
- package/dest/bin/check_txe_oracle_version.js +61 -0
- package/dest/bin/index.js +3 -30
- package/dest/bin/index.js.map +7 -0
- package/dest/bin/oracle_test_server.d.ts +3 -0
- package/dest/bin/oracle_test_server.d.ts.map +1 -0
- package/dest/bin/oracle_test_server.js +41 -0
- package/dest/chunk-BA54BEZH.js +265 -0
- package/dest/chunk-BA54BEZH.js.map +7 -0
- package/dest/chunk-BJVAAXNA.js +3 -0
- package/dest/chunk-BJVAAXNA.js.map +7 -0
- package/dest/chunk-ZKW35DT6.js +304 -0
- package/dest/chunk-ZKW35DT6.js.map +7 -0
- package/dest/constants.d.ts +5 -1
- package/dest/constants.d.ts.map +1 -1
- package/dest/constants.js +8 -0
- package/dest/dispatcher_pool.d.ts +67 -0
- package/dest/dispatcher_pool.d.ts.map +1 -0
- package/dest/dispatcher_pool.js +284 -0
- package/dest/index.d.ts +185 -7
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +70 -190
- package/dest/metafile.json +38691 -0
- package/dest/msgpackr_fr_extension.d.ts +2 -0
- package/dest/msgpackr_fr_extension.d.ts.map +1 -0
- package/dest/msgpackr_fr_extension.js +21 -0
- package/dest/oracle/interfaces.d.ts +33 -8
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
- package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
- package/dest/oracle/test-resolver/fixtures.js +39 -0
- package/dest/oracle/test-resolver/index.d.ts +9 -0
- package/dest/oracle/test-resolver/index.d.ts.map +1 -0
- package/dest/oracle/test-resolver/index.js +33 -0
- package/dest/oracle/test-resolver/resolver.d.ts +34 -0
- package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
- package/dest/oracle/test-resolver/resolver.js +114 -0
- package/dest/oracle/txe_oracle_public_context.d.ts +26 -2
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +43 -1
- package/dest/oracle/txe_oracle_registry.d.ts +147 -0
- package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_registry.js +562 -0
- package/dest/oracle/txe_oracle_top_level_context.d.ts +32 -12
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +146 -54
- package/dest/oracle/txe_oracle_version.d.ts +17 -0
- package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_version.js +14 -0
- package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
- package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
- package/dest/oracle/txe_private_execution_oracle.js +15 -0
- package/dest/rpc_server.d.ts +14 -0
- package/dest/rpc_server.d.ts.map +1 -0
- package/dest/rpc_server.js +78 -0
- package/dest/rpc_translator.d.ts +96 -230
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +708 -617
- package/dest/server.bundle.js +3 -0
- package/dest/server.bundle.js.map +7 -0
- package/dest/state_machine/archiver.d.ts +4 -3
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +26 -15
- package/dest/state_machine/dummy_p2p_client.d.ts +13 -7
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +16 -4
- package/dest/state_machine/global_variable_builder.d.ts +9 -4
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
- package/dest/state_machine/global_variable_builder.js +9 -3
- package/dest/state_machine/index.d.ts +4 -2
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +17 -3
- package/dest/state_machine/mock_epoch_cache.d.ts +16 -3
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +29 -2
- package/dest/state_machine/synchronizer.js +1 -1
- package/dest/txe_session.d.ts +74 -12
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +171 -36
- package/dest/utils/encoding.d.ts +191 -0
- package/dest/utils/encoding.d.ts.map +1 -0
- package/dest/{util → utils}/encoding.js +7 -2
- package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
- package/dest/utils/expected_failure_error.d.ts.map +1 -0
- package/dest/{util → utils}/txe_account_store.d.ts +1 -1
- package/dest/utils/txe_account_store.d.ts.map +1 -0
- package/dest/utils/txe_artifact_resolver.d.ts +37 -0
- package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
- package/dest/utils/txe_artifact_resolver.js +161 -0
- package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
- package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
- package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
- package/dest/worker.bundle.js +3 -0
- package/dest/worker.bundle.js.map +7 -0
- package/dest/worker.d.ts +2 -0
- package/dest/worker.d.ts.map +1 -0
- package/dest/worker.js +92 -0
- package/package.json +38 -21
- package/src/bin/check_txe_oracle_version.ts +70 -0
- package/src/bin/index.ts +11 -2
- package/src/bin/oracle_test_server.ts +51 -0
- package/src/constants.ts +10 -0
- package/src/dispatcher_pool.ts +316 -0
- package/src/index.ts +97 -227
- package/src/msgpackr_fr_extension.ts +23 -0
- package/src/oracle/interfaces.ts +29 -7
- package/src/oracle/test-resolver/fixtures.ts +84 -0
- package/src/oracle/test-resolver/index.ts +45 -0
- package/src/oracle/test-resolver/resolver.ts +165 -0
- package/src/oracle/txe_oracle_public_context.ts +60 -0
- package/src/oracle/txe_oracle_registry.ts +401 -0
- package/src/oracle/txe_oracle_top_level_context.ts +177 -61
- package/src/oracle/txe_oracle_version.ts +17 -0
- package/src/oracle/txe_private_execution_oracle.ts +30 -0
- package/src/rpc_server.ts +87 -0
- package/src/rpc_translator.ts +789 -904
- package/src/state_machine/archiver.ts +38 -16
- package/src/state_machine/dummy_p2p_client.ts +31 -11
- package/src/state_machine/global_variable_builder.ts +18 -3
- package/src/state_machine/index.ts +20 -5
- package/src/state_machine/mock_epoch_cache.ts +38 -3
- package/src/state_machine/synchronizer.ts +1 -1
- package/src/txe_session.ts +281 -38
- package/src/{util → utils}/encoding.ts +8 -2
- package/src/utils/txe_artifact_resolver.ts +217 -0
- package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
- package/src/worker.ts +98 -0
- package/dest/util/encoding.d.ts +0 -720
- package/dest/util/encoding.d.ts.map +0 -1
- package/dest/util/expected_failure_error.d.ts.map +0 -1
- package/dest/util/txe_account_store.d.ts.map +0 -1
- package/dest/util/txe_public_contract_data_source.d.ts +0 -20
- package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
- /package/dest/{util → utils}/expected_failure_error.js +0 -0
- /package/dest/{util → utils}/txe_account_store.js +0 -0
- /package/src/{util → utils}/expected_failure_error.ts +0 -0
- /package/src/{util → utils}/txe_account_store.ts +0 -0
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
|
|
3
|
-
|
|
4
|
-
DEFAULT_L2_GAS_LIMIT,
|
|
5
|
-
DEFAULT_TEARDOWN_DA_GAS_LIMIT,
|
|
6
|
-
DEFAULT_TEARDOWN_L2_GAS_LIMIT,
|
|
3
|
+
MAX_PRIVATE_LOGS_PER_TX,
|
|
7
4
|
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
8
5
|
} from '@aztec/constants';
|
|
9
6
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
@@ -12,17 +9,19 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
12
9
|
import { LogLevels, type Logger, applyStringFormatting, createLogger } from '@aztec/foundation/log';
|
|
13
10
|
import { TestDateProvider } from '@aztec/foundation/timer';
|
|
14
11
|
import type { KeyStore } from '@aztec/key-store';
|
|
15
|
-
import type { AccessScopes } from '@aztec/pxe/client/lazy';
|
|
16
12
|
import {
|
|
17
13
|
AddressStore,
|
|
14
|
+
CapsuleService,
|
|
18
15
|
CapsuleStore,
|
|
19
16
|
type ContractStore,
|
|
17
|
+
type ExecutionHooks,
|
|
20
18
|
NoteStore,
|
|
21
|
-
|
|
19
|
+
ORACLE_VERSION_MAJOR,
|
|
22
20
|
PrivateEventStore,
|
|
23
21
|
RecipientTaggingStore,
|
|
24
22
|
SenderAddressBookStore,
|
|
25
23
|
SenderTaggingStore,
|
|
24
|
+
composeHooks,
|
|
26
25
|
enrichPublicSimulationError,
|
|
27
26
|
} from '@aztec/pxe/server';
|
|
28
27
|
import {
|
|
@@ -64,6 +63,7 @@ import {
|
|
|
64
63
|
PrivateToPublicAccumulatedData,
|
|
65
64
|
PublicCallRequest,
|
|
66
65
|
} from '@aztec/stdlib/kernel';
|
|
66
|
+
import { hashPublicKey } from '@aztec/stdlib/keys';
|
|
67
67
|
import { ChonkProof } from '@aztec/stdlib/proofs';
|
|
68
68
|
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
69
69
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
@@ -80,13 +80,14 @@ import {
|
|
|
80
80
|
collectNested,
|
|
81
81
|
} from '@aztec/stdlib/tx';
|
|
82
82
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
83
|
-
import { ForkCheckpoint } from '@aztec/world-state';
|
|
83
|
+
import { ForkCheckpoint } from '@aztec/world-state/native';
|
|
84
84
|
|
|
85
|
-
import { DEFAULT_ADDRESS } from '../constants.js';
|
|
85
|
+
import { DEFAULT_ADDRESS, MAX_PRIVATE_EVENTS_PER_TXE_QUERY, MAX_PRIVATE_EVENT_LEN } from '../constants.js';
|
|
86
86
|
import type { TXEStateMachine } from '../state_machine/index.js';
|
|
87
|
-
import type { TXEAccountStore } from '../util/txe_account_store.js';
|
|
88
|
-
import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
|
|
89
87
|
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
|
|
88
|
+
import type { TXEAccountStore } from '../utils/txe_account_store.js';
|
|
89
|
+
import type { TXEArtifactResolver } from '../utils/txe_artifact_resolver.js';
|
|
90
|
+
import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js';
|
|
90
91
|
import type { ITxeExecutionOracle } from './interfaces.js';
|
|
91
92
|
|
|
92
93
|
export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle {
|
|
@@ -111,17 +112,32 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
111
112
|
private version: Fr,
|
|
112
113
|
private chainId: Fr,
|
|
113
114
|
private authwits: Map<string, AuthWitness>,
|
|
115
|
+
private readonly artifactResolver: TXEArtifactResolver,
|
|
116
|
+
private readonly rootPath: string,
|
|
117
|
+
private readonly packageName: string,
|
|
114
118
|
) {
|
|
115
119
|
this.logger = createLogger('txe:top_level_context');
|
|
116
120
|
this.logger.debug('Entering Top Level Context');
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
private contractOracleVersion: { major: number; minor: number } | undefined;
|
|
124
|
+
|
|
125
|
+
assertCompatibleOracleVersion(major: number, minor: number): void {
|
|
126
|
+
if (major !== ORACLE_VERSION_MAJOR) {
|
|
127
|
+
const hint =
|
|
128
|
+
major > ORACLE_VERSION_MAJOR
|
|
129
|
+
? '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.'
|
|
130
|
+
: '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.';
|
|
121
131
|
throw new Error(
|
|
122
|
-
`Incompatible
|
|
132
|
+
`Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`,
|
|
123
133
|
);
|
|
124
134
|
}
|
|
135
|
+
this.contractOracleVersion = { major, minor };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
|
|
139
|
+
nonOracleFunctionGetContractOracleVersion(): { major: number; minor: number } | undefined {
|
|
140
|
+
return this.contractOracleVersion;
|
|
125
141
|
}
|
|
126
142
|
|
|
127
143
|
// This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
|
|
@@ -131,7 +147,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
131
147
|
}
|
|
132
148
|
|
|
133
149
|
// We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
|
|
134
|
-
log(level: number, message: string, fields: Fr[]): Promise<void> {
|
|
150
|
+
log(level: number, message: string, _fieldsSize: number, fields: Fr[]): Promise<void> {
|
|
135
151
|
if (!LogLevels[level]) {
|
|
136
152
|
throw new Error(`Invalid log level: ${level}`);
|
|
137
153
|
}
|
|
@@ -154,12 +170,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
154
170
|
}
|
|
155
171
|
|
|
156
172
|
async getLastBlockTimestamp() {
|
|
157
|
-
return (await this.stateMachine.node.
|
|
173
|
+
return (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables.timestamp;
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
async getLastTxEffects() {
|
|
161
177
|
const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
|
|
162
|
-
const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
|
|
178
|
+
const block = await this.stateMachine.archiver.getBlock({ number: latestBlockNumber });
|
|
163
179
|
|
|
164
180
|
if (block!.body.txEffects.length != 1) {
|
|
165
181
|
// Note that calls like env.mine() will result in blocks with no transactions, hitting this
|
|
@@ -168,7 +184,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
168
184
|
|
|
169
185
|
const txEffects = block!.body.txEffects[0];
|
|
170
186
|
|
|
171
|
-
|
|
187
|
+
const privateLogs = txEffects.privateLogs;
|
|
188
|
+
if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
|
|
189
|
+
throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
txHash: txEffects.txHash,
|
|
194
|
+
noteHashes: txEffects.noteHashes,
|
|
195
|
+
nullifiers: txEffects.nullifiers,
|
|
196
|
+
privateLogs,
|
|
197
|
+
};
|
|
172
198
|
}
|
|
173
199
|
|
|
174
200
|
async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
|
|
@@ -191,7 +217,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
191
217
|
}
|
|
192
218
|
|
|
193
219
|
async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
|
|
194
|
-
|
|
220
|
+
const events = (
|
|
195
221
|
await this.privateEventStore.getPrivateEvents(selector, {
|
|
196
222
|
contractAddress,
|
|
197
223
|
scopes: [scope],
|
|
@@ -199,6 +225,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
199
225
|
toBlock: (await this.getLastBlockNumber()) + 1,
|
|
200
226
|
})
|
|
201
227
|
).map(e => e.packedEvent);
|
|
228
|
+
|
|
229
|
+
if (events.length > MAX_PRIVATE_EVENTS_PER_TXE_QUERY) {
|
|
230
|
+
throw new Error(`Array of length ${events.length} larger than maxLen ${MAX_PRIVATE_EVENTS_PER_TXE_QUERY}`);
|
|
231
|
+
}
|
|
232
|
+
if (events.some(e => e.length > MAX_PRIVATE_EVENT_LEN)) {
|
|
233
|
+
throw new Error(`Some private event has length larger than maxLen ${MAX_PRIVATE_EVENT_LEN}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return events;
|
|
202
237
|
}
|
|
203
238
|
|
|
204
239
|
async advanceBlocksBy(blocks: number) {
|
|
@@ -214,27 +249,71 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
214
249
|
this.nextBlockTimestamp += duration;
|
|
215
250
|
}
|
|
216
251
|
|
|
217
|
-
|
|
252
|
+
private deploymentNullifier(address: AztecAddress): Promise<Fr> {
|
|
253
|
+
return siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address.toField());
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async deploy(
|
|
257
|
+
contractPath: string,
|
|
258
|
+
initializer: string,
|
|
259
|
+
args: Fr[],
|
|
260
|
+
secret: Fr,
|
|
261
|
+
salt: Fr,
|
|
262
|
+
deployer: AztecAddress,
|
|
263
|
+
): Promise<Fr[]> {
|
|
264
|
+
const { artifact, instance } = await this.artifactResolver.resolveDeployArtifact({
|
|
265
|
+
rootPath: this.rootPath,
|
|
266
|
+
packageName: this.packageName,
|
|
267
|
+
contractPath,
|
|
268
|
+
initializer,
|
|
269
|
+
args,
|
|
270
|
+
secret,
|
|
271
|
+
salt,
|
|
272
|
+
deployer,
|
|
273
|
+
});
|
|
274
|
+
|
|
218
275
|
// Emit deployment nullifier
|
|
219
276
|
await this.mineBlock({
|
|
220
|
-
nullifiers: [
|
|
221
|
-
await siloNullifier(
|
|
222
|
-
AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
|
|
223
|
-
instance.address.toField(),
|
|
224
|
-
),
|
|
225
|
-
],
|
|
277
|
+
nullifiers: [await this.deploymentNullifier(instance.address)],
|
|
226
278
|
});
|
|
227
279
|
|
|
228
280
|
if (!secret.equals(Fr.ZERO)) {
|
|
229
|
-
await this.
|
|
281
|
+
await this.registerContractAndAddAccount(artifact, instance, secret);
|
|
230
282
|
} else {
|
|
231
283
|
await this.contractStore.addContractInstance(instance);
|
|
232
284
|
await this.contractStore.addContractArtifact(artifact);
|
|
233
285
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
234
286
|
}
|
|
287
|
+
|
|
288
|
+
return [
|
|
289
|
+
instance.salt,
|
|
290
|
+
instance.deployer.toField(),
|
|
291
|
+
instance.currentContractClassId,
|
|
292
|
+
instance.initializationHash,
|
|
293
|
+
instance.immutablesHash,
|
|
294
|
+
...instance.publicKeys.toFields(),
|
|
295
|
+
];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Mines a single block containing only the deployment nullifiers for the contracts at the given addresses.
|
|
300
|
+
*/
|
|
301
|
+
async mineDeploymentNullifiers(addresses: AztecAddress[]) {
|
|
302
|
+
await this.mineBlock({
|
|
303
|
+
nullifiers: await Promise.all(addresses.map(address => this.deploymentNullifier(address))),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async addAccount(secret: Fr) {
|
|
308
|
+
const { artifact, instance } = await this.artifactResolver.resolveAccountArtifact(secret);
|
|
309
|
+
return this.registerContractAndAddAccount(artifact, instance, secret);
|
|
235
310
|
}
|
|
236
311
|
|
|
237
|
-
async
|
|
312
|
+
private async registerContractAndAddAccount(
|
|
313
|
+
artifact: ContractArtifact,
|
|
314
|
+
instance: ContractInstanceWithAddress,
|
|
315
|
+
secret: Fr,
|
|
316
|
+
) {
|
|
238
317
|
const partialAddress = await computePartialAddress(instance);
|
|
239
318
|
|
|
240
319
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
@@ -261,12 +340,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
261
340
|
|
|
262
341
|
async addAuthWitness(address: AztecAddress, messageHash: Fr) {
|
|
263
342
|
const account = await this.accountStore.getAccount(address);
|
|
264
|
-
const
|
|
343
|
+
const ivpkMHash = await hashPublicKey(account.publicKeys.ivpkM);
|
|
344
|
+
const privateKey = await this.keyStore.getMasterSecretKey(ivpkMHash);
|
|
265
345
|
|
|
266
346
|
const schnorr = new Schnorr();
|
|
267
|
-
const signature = await schnorr.constructSignature(messageHash
|
|
347
|
+
const signature = await schnorr.constructSignature(messageHash, privateKey);
|
|
268
348
|
|
|
269
|
-
const authWitness = new AuthWitness(messageHash,
|
|
349
|
+
const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
|
|
270
350
|
|
|
271
351
|
this.authwits.set(authWitness.requestHash.toString(), authWitness);
|
|
272
352
|
}
|
|
@@ -297,13 +377,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
297
377
|
}
|
|
298
378
|
|
|
299
379
|
async privateCallNewFlow(
|
|
300
|
-
from: AztecAddress,
|
|
380
|
+
from: AztecAddress | undefined,
|
|
301
381
|
targetContractAddress: AztecAddress = AztecAddress.zero(),
|
|
302
382
|
functionSelector: FunctionSelector = FunctionSelector.empty(),
|
|
303
383
|
args: Fr[],
|
|
304
384
|
argsHash: Fr = Fr.zero(),
|
|
305
385
|
isStaticCall: boolean = false,
|
|
386
|
+
additionalScopes: AztecAddress[] = [],
|
|
306
387
|
jobId: string,
|
|
388
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
389
|
+
gasSettings: GasSettings,
|
|
307
390
|
) {
|
|
308
391
|
this.logger.verbose(
|
|
309
392
|
`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
@@ -317,12 +400,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
317
400
|
throw new Error(message);
|
|
318
401
|
}
|
|
319
402
|
|
|
320
|
-
|
|
321
|
-
// empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
|
|
322
|
-
const effectiveScopes = from.isZero() ? [] : [from];
|
|
403
|
+
const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
|
|
323
404
|
|
|
324
405
|
// Sync notes before executing private function to discover notes from previous transactions
|
|
325
|
-
const utilityExecutor = async (call: FunctionCall, execScopes:
|
|
406
|
+
const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
|
|
326
407
|
await this.executeUtilityCall(call, execScopes, jobId);
|
|
327
408
|
};
|
|
328
409
|
|
|
@@ -333,16 +414,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
333
414
|
utilityExecutor,
|
|
334
415
|
blockHeader,
|
|
335
416
|
jobId,
|
|
336
|
-
|
|
417
|
+
scopes,
|
|
337
418
|
);
|
|
338
419
|
|
|
339
420
|
const blockNumber = await this.getNextBlockNumber();
|
|
340
421
|
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
344
|
-
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
345
|
-
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
422
|
+
const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
|
|
423
|
+
const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
|
|
346
424
|
|
|
347
425
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
348
426
|
|
|
@@ -376,18 +454,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
376
454
|
senderTaggingStore: this.senderTaggingStore,
|
|
377
455
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
378
456
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
379
|
-
|
|
457
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
380
458
|
privateEventStore: this.privateEventStore,
|
|
381
459
|
contractSyncService: this.stateMachine.contractSyncService,
|
|
382
460
|
jobId,
|
|
383
461
|
totalPublicCalldataCount: 0,
|
|
384
462
|
sideEffectCounter: minRevertibleSideEffectCounter,
|
|
385
|
-
scopes
|
|
463
|
+
scopes,
|
|
386
464
|
// In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
387
465
|
// contract would perform, including setting senderForTags.
|
|
388
466
|
senderForTags: from,
|
|
389
467
|
simulator,
|
|
390
468
|
messageContextService: this.stateMachine.messageContextService,
|
|
469
|
+
l2TipsStore: this.stateMachine.l2TipsProvider,
|
|
470
|
+
hooks: composeHooks({
|
|
471
|
+
authorizeUtilityCall: this.buildAuthorizeUtilityCallHook(
|
|
472
|
+
isStaticCall ? 'private view' : 'private',
|
|
473
|
+
authorizedUtilityCallTargets,
|
|
474
|
+
),
|
|
475
|
+
}),
|
|
391
476
|
});
|
|
392
477
|
|
|
393
478
|
// 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.
|
|
@@ -410,7 +495,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
410
495
|
);
|
|
411
496
|
const publicFunctionsCalldata = await Promise.all(
|
|
412
497
|
publicCallRequests.map(async r => {
|
|
413
|
-
const calldata = await privateExecutionOracle.
|
|
498
|
+
const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
|
|
414
499
|
return new HashedValues(calldata, r.calldataHash);
|
|
415
500
|
}),
|
|
416
501
|
);
|
|
@@ -495,11 +580,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
495
580
|
}
|
|
496
581
|
}
|
|
497
582
|
|
|
583
|
+
// Walk the nested private-call tree and collect every offchain effect the transaction emitted.
|
|
584
|
+
// PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
|
|
585
|
+
// `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
|
|
586
|
+
// we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
|
|
587
|
+
const offchainEffects = collectNested([executionResult], r => r.offchainEffects.map(e => e.data));
|
|
588
|
+
|
|
498
589
|
if (isStaticCall) {
|
|
499
590
|
await checkpoint!.revert();
|
|
500
591
|
|
|
501
592
|
await forkedWorldTrees.close();
|
|
502
|
-
return executionResult.returnValues ?? [];
|
|
593
|
+
return { returnValues: executionResult.returnValues ?? [], offchainEffects };
|
|
503
594
|
}
|
|
504
595
|
|
|
505
596
|
const txEffect = TxEffect.empty();
|
|
@@ -521,14 +612,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
521
612
|
|
|
522
613
|
await forkedWorldTrees.close();
|
|
523
614
|
|
|
524
|
-
return executionResult.returnValues ?? [];
|
|
615
|
+
return { returnValues: executionResult.returnValues ?? [], offchainEffects };
|
|
525
616
|
}
|
|
526
617
|
|
|
527
618
|
async publicCallNewFlow(
|
|
528
|
-
from: AztecAddress,
|
|
619
|
+
from: AztecAddress | undefined,
|
|
529
620
|
targetContractAddress: AztecAddress,
|
|
530
621
|
calldata: Fr[],
|
|
531
622
|
isStaticCall: boolean,
|
|
623
|
+
gasSettings: GasSettings,
|
|
532
624
|
) {
|
|
533
625
|
this.logger.verbose(
|
|
534
626
|
`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
@@ -536,12 +628,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
536
628
|
|
|
537
629
|
const blockNumber = await this.getNextBlockNumber();
|
|
538
630
|
|
|
539
|
-
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
540
|
-
|
|
541
|
-
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
542
|
-
|
|
543
|
-
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
544
|
-
|
|
545
631
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
546
632
|
|
|
547
633
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
@@ -593,7 +679,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
593
679
|
// may require producing reverts.
|
|
594
680
|
const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
595
681
|
revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
|
|
596
|
-
from,
|
|
682
|
+
from ?? AztecAddress.NULL_MSG_SENDER,
|
|
597
683
|
targetContractAddress,
|
|
598
684
|
isStaticCall,
|
|
599
685
|
calldataHash,
|
|
@@ -684,6 +770,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
684
770
|
functionSelector: FunctionSelector,
|
|
685
771
|
args: Fr[],
|
|
686
772
|
jobId: string,
|
|
773
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
687
774
|
) {
|
|
688
775
|
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
689
776
|
if (!artifact) {
|
|
@@ -700,7 +787,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
700
787
|
},
|
|
701
788
|
blockHeader,
|
|
702
789
|
jobId,
|
|
703
|
-
|
|
790
|
+
await this.keyStore.getAccounts(),
|
|
704
791
|
);
|
|
705
792
|
|
|
706
793
|
const call = FunctionCall.from({
|
|
@@ -714,10 +801,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
714
801
|
returnTypes: [],
|
|
715
802
|
});
|
|
716
803
|
|
|
717
|
-
return this.executeUtilityCall(call,
|
|
804
|
+
return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
|
|
718
805
|
}
|
|
719
806
|
|
|
720
|
-
private async executeUtilityCall(
|
|
807
|
+
private async executeUtilityCall(
|
|
808
|
+
call: FunctionCall,
|
|
809
|
+
scopes: AztecAddress[],
|
|
810
|
+
jobId: string,
|
|
811
|
+
authorizedUtilityCallTargets: AztecAddress[] = [],
|
|
812
|
+
): Promise<Fr[]> {
|
|
721
813
|
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
722
814
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
723
815
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
@@ -730,6 +822,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
730
822
|
|
|
731
823
|
try {
|
|
732
824
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
825
|
+
const simulator = new WASMSimulator();
|
|
826
|
+
const utilityExecutor = async (syncCall: FunctionCall, execScopes: AztecAddress[]) => {
|
|
827
|
+
await this.executeUtilityCall(syncCall, execScopes, jobId);
|
|
828
|
+
};
|
|
733
829
|
const oracle = new UtilityExecutionOracle({
|
|
734
830
|
contractAddress: call.to,
|
|
735
831
|
authWitnesses: [],
|
|
@@ -742,13 +838,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
742
838
|
aztecNode: this.stateMachine.node,
|
|
743
839
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
744
840
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
745
|
-
|
|
841
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
746
842
|
privateEventStore: this.privateEventStore,
|
|
747
843
|
messageContextService: this.stateMachine.messageContextService,
|
|
844
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
845
|
+
l2TipsStore: this.stateMachine.l2TipsProvider,
|
|
748
846
|
jobId,
|
|
749
847
|
scopes,
|
|
848
|
+
simulator,
|
|
849
|
+
hooks: composeHooks({
|
|
850
|
+
authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets),
|
|
851
|
+
}),
|
|
852
|
+
utilityExecutor,
|
|
750
853
|
});
|
|
751
|
-
const acirExecutionResult = await
|
|
854
|
+
const acirExecutionResult = await simulator
|
|
752
855
|
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
753
856
|
.catch((err: Error) => {
|
|
754
857
|
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
@@ -776,7 +879,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
776
879
|
}
|
|
777
880
|
|
|
778
881
|
private async getLastBlockNumber(): Promise<BlockNumber> {
|
|
779
|
-
const
|
|
780
|
-
return
|
|
882
|
+
const block = await this.stateMachine.node.getBlock('latest');
|
|
883
|
+
return block ? block.header.globalVariables.blockNumber : BlockNumber.ZERO;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
private buildAuthorizeUtilityCallHook(
|
|
887
|
+
callerContext: 'private' | 'private view' | 'utility',
|
|
888
|
+
authorizedTargets: AztecAddress[],
|
|
889
|
+
): ExecutionHooks['authorizeUtilityCall'] | undefined {
|
|
890
|
+
if (authorizedTargets.length === 0) {
|
|
891
|
+
return undefined;
|
|
892
|
+
}
|
|
893
|
+
return req =>
|
|
894
|
+
Promise.resolve({
|
|
895
|
+
authorized: req.callerContext === callerContext && authorizedTargets.some(t => t.equals(req.target)),
|
|
896
|
+
});
|
|
781
897
|
}
|
|
782
898
|
}
|
|
@@ -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 const TXE_ORACLE_VERSION_MAJOR = 1;
|
|
9
|
+
export const TXE_ORACLE_VERSION_MINOR = 2;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This hash is computed from the TXE oracle interfaces (IAvmExecutionOracle and ITxeExecutionOracle) and is used to
|
|
13
|
+
* detect when those interfaces change. When it does, bump:
|
|
14
|
+
* - TXE_ORACLE_VERSION_MAJOR (and reset MINOR to 0) for breaking changes, or
|
|
15
|
+
* - TXE_ORACLE_VERSION_MINOR for additive changes (new oracle method added).
|
|
16
|
+
*/
|
|
17
|
+
export const TXE_ORACLE_INTERFACE_HASH = '9e5f6ad5fd170d1de5ddd417f19cce47b17382567e08360dc6a783154828e218';
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
/**
|
|
7
|
+
* TXE-specific subclass of PrivateExecutionOracle that forbids operations not supported in
|
|
8
|
+
* TestEnvironment::private_context. TXE uses dedicated oracle flows (e.g. private_call) instead.
|
|
9
|
+
*/
|
|
10
|
+
export class TXEPrivateExecutionOracle extends PrivateExecutionOracle {
|
|
11
|
+
override callPrivateFunction(
|
|
12
|
+
_targetContractAddress: AztecAddress,
|
|
13
|
+
_functionSelector: FunctionSelector,
|
|
14
|
+
_argsHash: Fr,
|
|
15
|
+
_sideEffectCounter: number,
|
|
16
|
+
_isStaticCall: boolean,
|
|
17
|
+
): Promise<{ endSideEffectCounter: Fr; returnsHash: Fr }> {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'Contract calls are forbidden inside a `TestEnvironment::private_context`, use `private_call` instead',
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
override assertValidPublicCalldata(_calldataHash: Fr): Promise<void> {
|
|
24
|
+
throw new Error('Enqueueing public calls is not supported in TestEnvironment::private_context');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override notifyRevertiblePhaseStart(_minRevertibleSideEffectCounter: number): Promise<void> {
|
|
28
|
+
throw new Error('Enqueueing public calls is not supported in TestEnvironment::private_context');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
|
|
2
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
3
|
+
|
|
4
|
+
import type { Socket } from 'node:net';
|
|
5
|
+
|
|
6
|
+
import { TXEDispatcherPool, buildSharedContractStore } from './dispatcher_pool.js';
|
|
7
|
+
import { TXEDispatcher, TXEDispatcherApiSchema } from './index.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Symbol used to tag an incoming TCP socket with the `session_id` it has been associated with.
|
|
11
|
+
* Hidden under a Symbol so we don't risk colliding with anything koa or http core adds.
|
|
12
|
+
*/
|
|
13
|
+
const SESSION_SYMBOL = Symbol('txeSessionId');
|
|
14
|
+
|
|
15
|
+
type TaggedSocket = Socket & { [SESSION_SYMBOL]?: number };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates the TXE RPC server. With `TXE_WORKERS=1` oracle calls run on the main thread (no
|
|
19
|
+
* worker_threads, no IPC overhead). With any other value oracle calls are
|
|
20
|
+
* routed to a pool of worker threads sized to that value, sticky by `session_id`.
|
|
21
|
+
*
|
|
22
|
+
* Each incoming TCP socket is tagged with the `session_id` of the first oracle call it carries —
|
|
23
|
+
* nargo uses one HTTP client per test, so the socket-to-session mapping is 1:1. When the socket
|
|
24
|
+
* closes (end of test), the dispatcher disposes the session and frees its world state + LMDB.
|
|
25
|
+
*
|
|
26
|
+
* Lives in its own module so the worker bundle does not pull in the HTTP server stack.
|
|
27
|
+
*/
|
|
28
|
+
export async function createTXERpcServer(logger: Logger) {
|
|
29
|
+
const workerCount = Number(process.env.TXE_WORKERS);
|
|
30
|
+
let dispatcher: TXEDispatcher | TXEDispatcherPool;
|
|
31
|
+
if (workerCount === 1) {
|
|
32
|
+
const { dataDir, schnorrClassId } = await buildSharedContractStore();
|
|
33
|
+
dispatcher = new TXEDispatcher(logger, { contractStoreSourceDir: dataDir, schnorrClassId });
|
|
34
|
+
} else {
|
|
35
|
+
dispatcher = new TXEDispatcherPool(logger, {
|
|
36
|
+
workers: Number.isFinite(workerCount) && workerCount > 1 ? workerCount : undefined,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const server = createSafeJsonRpcServer(dispatcher, TXEDispatcherApiSchema, {
|
|
40
|
+
http200OnError: true,
|
|
41
|
+
middlewares: [
|
|
42
|
+
async (ctx, next) => {
|
|
43
|
+
// The body parser runs further down the chain, so `ctx.request.body` is populated only
|
|
44
|
+
// after `next()` resolves.
|
|
45
|
+
await next();
|
|
46
|
+
const socket = ctx.req.socket as TaggedSocket;
|
|
47
|
+
if (socket[SESSION_SYMBOL] !== undefined) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const body = (ctx.request as { body?: unknown }).body;
|
|
51
|
+
const sessionId =
|
|
52
|
+
body && typeof body === 'object' && 'params' in body
|
|
53
|
+
? extractSessionId((body as { params: unknown }).params)
|
|
54
|
+
: undefined;
|
|
55
|
+
if (sessionId === undefined) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
socket[SESSION_SYMBOL] = sessionId;
|
|
59
|
+
logger.debug(`Tagged socket with session`, {
|
|
60
|
+
sessionId,
|
|
61
|
+
remoteAddress: socket.remoteAddress,
|
|
62
|
+
remotePort: socket.remotePort,
|
|
63
|
+
});
|
|
64
|
+
socket.once('close', () => {
|
|
65
|
+
logger.debug(`Disposing session on socket close`, { sessionId });
|
|
66
|
+
void dispatcher.disposeSession(sessionId);
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
return server;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Extracts `session_id` from a JSON-RPC `params` array. Always a `number` because session_id
|
|
75
|
+
// comes off `JSON.parse`, which never produces BigInt; values above MAX_SAFE_INTEGER lose
|
|
76
|
+
// precision but still work as a Map key.
|
|
77
|
+
function extractSessionId(params: unknown): number | undefined {
|
|
78
|
+
if (!Array.isArray(params) || params.length === 0) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const first = params[0];
|
|
82
|
+
if (first && typeof first === 'object' && 'session_id' in first) {
|
|
83
|
+
const sid = (first as { session_id: unknown }).session_id;
|
|
84
|
+
return typeof sid === 'number' ? sid : undefined;
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|