@aztec/txe 5.0.0-private.20260318 → 5.0.0-rc.1
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/AuthRegistry-CPGFQR26.js +3 -0
- package/dest/AuthRegistry-CPGFQR26.js.map +7 -0
- package/dest/ContractClassRegistry-EHVIHGEK.js +3 -0
- package/dest/ContractClassRegistry-EHVIHGEK.js.map +7 -0
- package/dest/ContractInstanceRegistry-DWZDXHRG.js +3 -0
- package/dest/ContractInstanceRegistry-DWZDXHRG.js.map +7 -0
- package/dest/FeeJuice-MI32ZO7B.js +3 -0
- package/dest/FeeJuice-MI32ZO7B.js.map +7 -0
- package/dest/HandshakeRegistry-3KSP3ITH.js +3 -0
- package/dest/HandshakeRegistry-3KSP3ITH.js.map +7 -0
- package/dest/MultiCallEntrypoint-IU7HYFYE.js +3 -0
- package/dest/MultiCallEntrypoint-IU7HYFYE.js.map +7 -0
- package/dest/SchnorrAccount-6TUE7JX4.js +3 -0
- package/dest/SchnorrAccount-6TUE7JX4.js.map +7 -0
- package/dest/SchnorrInitializerlessAccount-S3DU2DJK.js +3 -0
- package/dest/SchnorrInitializerlessAccount-S3DU2DJK.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-5U25VAFR.js +265 -0
- package/dest/chunk-5U25VAFR.js.map +7 -0
- package/dest/chunk-BJVAAXNA.js +3 -0
- package/dest/chunk-BJVAAXNA.js.map +7 -0
- package/dest/chunk-UPW55EJX.js +304 -0
- package/dest/chunk-UPW55EJX.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 +286 -0
- package/dest/index.d.ts +51 -7
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +70 -190
- package/dest/metafile.json +38829 -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 +14 -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 -18
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +151 -55
- 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 +103 -230
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +697 -616
- 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 +14 -7
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +19 -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 +11 -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 +85 -17
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +245 -40
- 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 +317 -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 +185 -64
- 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 +767 -892
- package/src/state_machine/archiver.ts +38 -16
- package/src/state_machine/dummy_p2p_client.ts +35 -11
- package/src/state_machine/global_variable_builder.ts +18 -3
- package/src/state_machine/index.ts +17 -5
- package/src/state_machine/mock_epoch_cache.ts +38 -3
- package/src/state_machine/synchronizer.ts +1 -1
- package/src/txe_session.ts +437 -50
- 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 {
|
|
@@ -30,9 +29,10 @@ import {
|
|
|
30
29
|
ExecutionTaggingIndexCache,
|
|
31
30
|
HashedValuesCache,
|
|
32
31
|
type IMiscOracle,
|
|
33
|
-
Oracle,
|
|
34
32
|
PrivateExecutionOracle,
|
|
33
|
+
TransientArrayService,
|
|
35
34
|
UtilityExecutionOracle,
|
|
35
|
+
buildACIRCallback,
|
|
36
36
|
executePrivateFunction,
|
|
37
37
|
generateSimulatedProvingResult,
|
|
38
38
|
} from '@aztec/pxe/simulator';
|
|
@@ -64,6 +64,7 @@ import {
|
|
|
64
64
|
PrivateToPublicAccumulatedData,
|
|
65
65
|
PublicCallRequest,
|
|
66
66
|
} from '@aztec/stdlib/kernel';
|
|
67
|
+
import { hashPublicKey } from '@aztec/stdlib/keys';
|
|
67
68
|
import { ChonkProof } from '@aztec/stdlib/proofs';
|
|
68
69
|
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
69
70
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
@@ -80,13 +81,14 @@ import {
|
|
|
80
81
|
collectNested,
|
|
81
82
|
} from '@aztec/stdlib/tx';
|
|
82
83
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
83
|
-
import { ForkCheckpoint } from '@aztec/world-state';
|
|
84
|
+
import { ForkCheckpoint } from '@aztec/world-state/native';
|
|
84
85
|
|
|
85
|
-
import { DEFAULT_ADDRESS } from '../constants.js';
|
|
86
|
+
import { DEFAULT_ADDRESS, MAX_PRIVATE_EVENTS_PER_TXE_QUERY, MAX_PRIVATE_EVENT_LEN } from '../constants.js';
|
|
86
87
|
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
88
|
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
|
|
89
|
+
import type { TXEAccountStore } from '../utils/txe_account_store.js';
|
|
90
|
+
import type { TXEArtifactResolver } from '../utils/txe_artifact_resolver.js';
|
|
91
|
+
import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js';
|
|
90
92
|
import type { ITxeExecutionOracle } from './interfaces.js';
|
|
91
93
|
|
|
92
94
|
export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle {
|
|
@@ -111,17 +113,32 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
111
113
|
private version: Fr,
|
|
112
114
|
private chainId: Fr,
|
|
113
115
|
private authwits: Map<string, AuthWitness>,
|
|
116
|
+
private readonly artifactResolver: TXEArtifactResolver,
|
|
117
|
+
private readonly rootPath: string,
|
|
118
|
+
private readonly packageName: string,
|
|
114
119
|
) {
|
|
115
120
|
this.logger = createLogger('txe:top_level_context');
|
|
116
121
|
this.logger.debug('Entering Top Level Context');
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
private contractOracleVersion: { major: number; minor: number } | undefined;
|
|
125
|
+
|
|
126
|
+
assertCompatibleOracleVersion(major: number, minor: number): void {
|
|
127
|
+
if (major !== ORACLE_VERSION_MAJOR) {
|
|
128
|
+
const hint =
|
|
129
|
+
major > ORACLE_VERSION_MAJOR
|
|
130
|
+
? '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.'
|
|
131
|
+
: '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
132
|
throw new Error(
|
|
122
|
-
`Incompatible
|
|
133
|
+
`Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`,
|
|
123
134
|
);
|
|
124
135
|
}
|
|
136
|
+
this.contractOracleVersion = { major, minor };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
|
|
140
|
+
nonOracleFunctionGetContractOracleVersion(): { major: number; minor: number } | undefined {
|
|
141
|
+
return this.contractOracleVersion;
|
|
125
142
|
}
|
|
126
143
|
|
|
127
144
|
// This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
|
|
@@ -131,7 +148,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
131
148
|
}
|
|
132
149
|
|
|
133
150
|
// 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> {
|
|
151
|
+
log(level: number, message: string, _fieldsSize: number, fields: Fr[]): Promise<void> {
|
|
135
152
|
if (!LogLevels[level]) {
|
|
136
153
|
throw new Error(`Invalid log level: ${level}`);
|
|
137
154
|
}
|
|
@@ -154,12 +171,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
154
171
|
}
|
|
155
172
|
|
|
156
173
|
async getLastBlockTimestamp() {
|
|
157
|
-
return (await this.stateMachine.node.
|
|
174
|
+
return (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables.timestamp;
|
|
158
175
|
}
|
|
159
176
|
|
|
160
|
-
async getLastTxEffects() {
|
|
177
|
+
async getLastTxEffects(): ReturnType<ITxeExecutionOracle['getLastTxEffects']> {
|
|
161
178
|
const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
|
|
162
|
-
const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
|
|
179
|
+
const block = await this.stateMachine.archiver.getBlock({ number: latestBlockNumber });
|
|
163
180
|
|
|
164
181
|
if (block!.body.txEffects.length != 1) {
|
|
165
182
|
// Note that calls like env.mine() will result in blocks with no transactions, hitting this
|
|
@@ -168,7 +185,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
168
185
|
|
|
169
186
|
const txEffects = block!.body.txEffects[0];
|
|
170
187
|
|
|
171
|
-
|
|
188
|
+
const privateLogs = txEffects.privateLogs;
|
|
189
|
+
if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
|
|
190
|
+
throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
txHash: txEffects.txHash,
|
|
195
|
+
noteHashes: txEffects.noteHashes,
|
|
196
|
+
nullifiers: txEffects.nullifiers,
|
|
197
|
+
privateLogs,
|
|
198
|
+
};
|
|
172
199
|
}
|
|
173
200
|
|
|
174
201
|
async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
|
|
@@ -191,7 +218,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
191
218
|
}
|
|
192
219
|
|
|
193
220
|
async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
|
|
194
|
-
|
|
221
|
+
const events = (
|
|
195
222
|
await this.privateEventStore.getPrivateEvents(selector, {
|
|
196
223
|
contractAddress,
|
|
197
224
|
scopes: [scope],
|
|
@@ -199,6 +226,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
199
226
|
toBlock: (await this.getLastBlockNumber()) + 1,
|
|
200
227
|
})
|
|
201
228
|
).map(e => e.packedEvent);
|
|
229
|
+
|
|
230
|
+
if (events.length > MAX_PRIVATE_EVENTS_PER_TXE_QUERY) {
|
|
231
|
+
throw new Error(`Array of length ${events.length} larger than maxLen ${MAX_PRIVATE_EVENTS_PER_TXE_QUERY}`);
|
|
232
|
+
}
|
|
233
|
+
if (events.some(e => e.length > MAX_PRIVATE_EVENT_LEN)) {
|
|
234
|
+
throw new Error(`Some private event has length larger than maxLen ${MAX_PRIVATE_EVENT_LEN}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return events;
|
|
202
238
|
}
|
|
203
239
|
|
|
204
240
|
async advanceBlocksBy(blocks: number) {
|
|
@@ -214,27 +250,71 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
214
250
|
this.nextBlockTimestamp += duration;
|
|
215
251
|
}
|
|
216
252
|
|
|
217
|
-
|
|
253
|
+
private deploymentNullifier(address: AztecAddress): Promise<Fr> {
|
|
254
|
+
return siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address.toField());
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async deploy(
|
|
258
|
+
contractPath: string,
|
|
259
|
+
initializer: string,
|
|
260
|
+
args: Fr[],
|
|
261
|
+
secret: Fr,
|
|
262
|
+
salt: Fr,
|
|
263
|
+
deployer: AztecAddress,
|
|
264
|
+
): Promise<Fr[]> {
|
|
265
|
+
const { artifact, instance } = await this.artifactResolver.resolveDeployArtifact({
|
|
266
|
+
rootPath: this.rootPath,
|
|
267
|
+
packageName: this.packageName,
|
|
268
|
+
contractPath,
|
|
269
|
+
initializer,
|
|
270
|
+
args,
|
|
271
|
+
secret,
|
|
272
|
+
salt,
|
|
273
|
+
deployer,
|
|
274
|
+
});
|
|
275
|
+
|
|
218
276
|
// Emit deployment nullifier
|
|
219
277
|
await this.mineBlock({
|
|
220
|
-
nullifiers: [
|
|
221
|
-
await siloNullifier(
|
|
222
|
-
AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
|
|
223
|
-
instance.address.toField(),
|
|
224
|
-
),
|
|
225
|
-
],
|
|
278
|
+
nullifiers: [await this.deploymentNullifier(instance.address)],
|
|
226
279
|
});
|
|
227
280
|
|
|
228
281
|
if (!secret.equals(Fr.ZERO)) {
|
|
229
|
-
await this.
|
|
282
|
+
await this.registerContractAndAddAccount(artifact, instance, secret);
|
|
230
283
|
} else {
|
|
231
284
|
await this.contractStore.addContractInstance(instance);
|
|
232
285
|
await this.contractStore.addContractArtifact(artifact);
|
|
233
286
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
234
287
|
}
|
|
288
|
+
|
|
289
|
+
return [
|
|
290
|
+
instance.salt,
|
|
291
|
+
instance.deployer.toField(),
|
|
292
|
+
instance.currentContractClassId,
|
|
293
|
+
instance.initializationHash,
|
|
294
|
+
instance.immutablesHash,
|
|
295
|
+
...instance.publicKeys.toFields(),
|
|
296
|
+
];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Mines a single block containing only the deployment nullifiers for the contracts at the given addresses.
|
|
301
|
+
*/
|
|
302
|
+
async mineDeploymentNullifiers(addresses: AztecAddress[]) {
|
|
303
|
+
await this.mineBlock({
|
|
304
|
+
nullifiers: await Promise.all(addresses.map(address => this.deploymentNullifier(address))),
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async addAccount(secret: Fr) {
|
|
309
|
+
const { artifact, instance } = await this.artifactResolver.resolveAccountArtifact(secret);
|
|
310
|
+
return this.registerContractAndAddAccount(artifact, instance, secret);
|
|
235
311
|
}
|
|
236
312
|
|
|
237
|
-
async
|
|
313
|
+
private async registerContractAndAddAccount(
|
|
314
|
+
artifact: ContractArtifact,
|
|
315
|
+
instance: ContractInstanceWithAddress,
|
|
316
|
+
secret: Fr,
|
|
317
|
+
) {
|
|
238
318
|
const partialAddress = await computePartialAddress(instance);
|
|
239
319
|
|
|
240
320
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
@@ -261,12 +341,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
261
341
|
|
|
262
342
|
async addAuthWitness(address: AztecAddress, messageHash: Fr) {
|
|
263
343
|
const account = await this.accountStore.getAccount(address);
|
|
264
|
-
const
|
|
344
|
+
const ivpkMHash = await hashPublicKey(account.publicKeys.ivpkM);
|
|
345
|
+
const privateKey = await this.keyStore.getMasterSecretKey(ivpkMHash);
|
|
265
346
|
|
|
266
347
|
const schnorr = new Schnorr();
|
|
267
|
-
const signature = await schnorr.constructSignature(messageHash
|
|
348
|
+
const signature = await schnorr.constructSignature(messageHash, privateKey);
|
|
268
349
|
|
|
269
|
-
const authWitness = new AuthWitness(messageHash,
|
|
350
|
+
const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
|
|
270
351
|
|
|
271
352
|
this.authwits.set(authWitness.requestHash.toString(), authWitness);
|
|
272
353
|
}
|
|
@@ -297,13 +378,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
297
378
|
}
|
|
298
379
|
|
|
299
380
|
async privateCallNewFlow(
|
|
300
|
-
from: AztecAddress,
|
|
381
|
+
from: AztecAddress | undefined,
|
|
301
382
|
targetContractAddress: AztecAddress = AztecAddress.zero(),
|
|
302
383
|
functionSelector: FunctionSelector = FunctionSelector.empty(),
|
|
303
384
|
args: Fr[],
|
|
304
385
|
argsHash: Fr = Fr.zero(),
|
|
305
386
|
isStaticCall: boolean = false,
|
|
387
|
+
additionalScopes: AztecAddress[] = [],
|
|
306
388
|
jobId: string,
|
|
389
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
390
|
+
gasSettings: GasSettings,
|
|
307
391
|
) {
|
|
308
392
|
this.logger.verbose(
|
|
309
393
|
`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
@@ -317,12 +401,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
317
401
|
throw new Error(message);
|
|
318
402
|
}
|
|
319
403
|
|
|
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];
|
|
404
|
+
const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
|
|
323
405
|
|
|
324
406
|
// Sync notes before executing private function to discover notes from previous transactions
|
|
325
|
-
const utilityExecutor = async (call: FunctionCall, execScopes:
|
|
407
|
+
const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
|
|
326
408
|
await this.executeUtilityCall(call, execScopes, jobId);
|
|
327
409
|
};
|
|
328
410
|
|
|
@@ -333,16 +415,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
333
415
|
utilityExecutor,
|
|
334
416
|
blockHeader,
|
|
335
417
|
jobId,
|
|
336
|
-
|
|
418
|
+
scopes,
|
|
337
419
|
);
|
|
338
420
|
|
|
339
421
|
const blockNumber = await this.getNextBlockNumber();
|
|
340
422
|
|
|
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());
|
|
423
|
+
const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
|
|
424
|
+
const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
|
|
346
425
|
|
|
347
426
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
348
427
|
|
|
@@ -357,6 +436,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
357
436
|
|
|
358
437
|
const simulator = new WASMSimulator();
|
|
359
438
|
|
|
439
|
+
const transientArrayService = new TransientArrayService();
|
|
360
440
|
const privateExecutionOracle = new PrivateExecutionOracle({
|
|
361
441
|
argsHash,
|
|
362
442
|
txContext,
|
|
@@ -376,18 +456,26 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
376
456
|
senderTaggingStore: this.senderTaggingStore,
|
|
377
457
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
378
458
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
379
|
-
|
|
459
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
380
460
|
privateEventStore: this.privateEventStore,
|
|
381
461
|
contractSyncService: this.stateMachine.contractSyncService,
|
|
382
462
|
jobId,
|
|
383
463
|
totalPublicCalldataCount: 0,
|
|
384
464
|
sideEffectCounter: minRevertibleSideEffectCounter,
|
|
385
|
-
scopes
|
|
465
|
+
scopes,
|
|
386
466
|
// In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
387
467
|
// contract would perform, including setting senderForTags.
|
|
388
468
|
senderForTags: from,
|
|
389
469
|
simulator,
|
|
390
470
|
messageContextService: this.stateMachine.messageContextService,
|
|
471
|
+
l2TipsStore: this.stateMachine.l2TipsProvider,
|
|
472
|
+
hooks: composeHooks({
|
|
473
|
+
authorizeUtilityCall: this.buildAuthorizeUtilityCallHook(
|
|
474
|
+
isStaticCall ? 'private view' : 'private',
|
|
475
|
+
authorizedUtilityCallTargets,
|
|
476
|
+
),
|
|
477
|
+
}),
|
|
478
|
+
transientArrayService,
|
|
391
479
|
});
|
|
392
480
|
|
|
393
481
|
// 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 +498,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
410
498
|
);
|
|
411
499
|
const publicFunctionsCalldata = await Promise.all(
|
|
412
500
|
publicCallRequests.map(async r => {
|
|
413
|
-
const calldata = await privateExecutionOracle.
|
|
501
|
+
const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
|
|
414
502
|
return new HashedValues(calldata, r.calldataHash);
|
|
415
503
|
}),
|
|
416
504
|
);
|
|
@@ -495,11 +583,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
495
583
|
}
|
|
496
584
|
}
|
|
497
585
|
|
|
586
|
+
// Walk the nested private-call tree and collect every offchain effect the transaction emitted.
|
|
587
|
+
// PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
|
|
588
|
+
// `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
|
|
589
|
+
// we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
|
|
590
|
+
const offchainEffects = collectNested([executionResult], r => r.offchainEffects.map(e => e.data));
|
|
591
|
+
|
|
498
592
|
if (isStaticCall) {
|
|
499
593
|
await checkpoint!.revert();
|
|
500
594
|
|
|
501
595
|
await forkedWorldTrees.close();
|
|
502
|
-
return executionResult.returnValues ?? [];
|
|
596
|
+
return { returnValues: executionResult.returnValues ?? [], offchainEffects };
|
|
503
597
|
}
|
|
504
598
|
|
|
505
599
|
const txEffect = TxEffect.empty();
|
|
@@ -521,14 +615,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
521
615
|
|
|
522
616
|
await forkedWorldTrees.close();
|
|
523
617
|
|
|
524
|
-
return executionResult.returnValues ?? [];
|
|
618
|
+
return { returnValues: executionResult.returnValues ?? [], offchainEffects };
|
|
525
619
|
}
|
|
526
620
|
|
|
527
621
|
async publicCallNewFlow(
|
|
528
|
-
from: AztecAddress,
|
|
622
|
+
from: AztecAddress | undefined,
|
|
529
623
|
targetContractAddress: AztecAddress,
|
|
530
624
|
calldata: Fr[],
|
|
531
625
|
isStaticCall: boolean,
|
|
626
|
+
gasSettings: GasSettings,
|
|
532
627
|
) {
|
|
533
628
|
this.logger.verbose(
|
|
534
629
|
`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
@@ -536,12 +631,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
536
631
|
|
|
537
632
|
const blockNumber = await this.getNextBlockNumber();
|
|
538
633
|
|
|
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
634
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
546
635
|
|
|
547
636
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
@@ -593,7 +682,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
593
682
|
// may require producing reverts.
|
|
594
683
|
const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
595
684
|
revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
|
|
596
|
-
from,
|
|
685
|
+
from ?? AztecAddress.NULL_MSG_SENDER,
|
|
597
686
|
targetContractAddress,
|
|
598
687
|
isStaticCall,
|
|
599
688
|
calldataHash,
|
|
@@ -684,6 +773,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
684
773
|
functionSelector: FunctionSelector,
|
|
685
774
|
args: Fr[],
|
|
686
775
|
jobId: string,
|
|
776
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
687
777
|
) {
|
|
688
778
|
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
689
779
|
if (!artifact) {
|
|
@@ -700,7 +790,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
700
790
|
},
|
|
701
791
|
blockHeader,
|
|
702
792
|
jobId,
|
|
703
|
-
|
|
793
|
+
await this.keyStore.getAccounts(),
|
|
704
794
|
);
|
|
705
795
|
|
|
706
796
|
const call = FunctionCall.from({
|
|
@@ -714,10 +804,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
714
804
|
returnTypes: [],
|
|
715
805
|
});
|
|
716
806
|
|
|
717
|
-
return this.executeUtilityCall(call,
|
|
807
|
+
return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
|
|
718
808
|
}
|
|
719
809
|
|
|
720
|
-
private async executeUtilityCall(
|
|
810
|
+
private async executeUtilityCall(
|
|
811
|
+
call: FunctionCall,
|
|
812
|
+
scopes: AztecAddress[],
|
|
813
|
+
jobId: string,
|
|
814
|
+
authorizedUtilityCallTargets: AztecAddress[] = [],
|
|
815
|
+
): Promise<Fr[]> {
|
|
721
816
|
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
722
817
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
723
818
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
@@ -730,6 +825,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
730
825
|
|
|
731
826
|
try {
|
|
732
827
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
828
|
+
const simulator = new WASMSimulator();
|
|
829
|
+
const utilityExecutor = async (syncCall: FunctionCall, execScopes: AztecAddress[]) => {
|
|
830
|
+
await this.executeUtilityCall(syncCall, execScopes, jobId);
|
|
831
|
+
};
|
|
733
832
|
const oracle = new UtilityExecutionOracle({
|
|
734
833
|
contractAddress: call.to,
|
|
735
834
|
authWitnesses: [],
|
|
@@ -742,14 +841,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
742
841
|
aztecNode: this.stateMachine.node,
|
|
743
842
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
744
843
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
745
|
-
|
|
844
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
746
845
|
privateEventStore: this.privateEventStore,
|
|
747
846
|
messageContextService: this.stateMachine.messageContextService,
|
|
847
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
848
|
+
l2TipsStore: this.stateMachine.l2TipsProvider,
|
|
748
849
|
jobId,
|
|
749
850
|
scopes,
|
|
851
|
+
simulator,
|
|
852
|
+
hooks: composeHooks({
|
|
853
|
+
authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets),
|
|
854
|
+
}),
|
|
855
|
+
utilityExecutor,
|
|
856
|
+
// Execution-tree root (top-level utility run or contract sync): own store; nested frames inherit it.
|
|
857
|
+
transientArrayService: new TransientArrayService(),
|
|
750
858
|
});
|
|
751
|
-
const acirExecutionResult = await
|
|
752
|
-
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact,
|
|
859
|
+
const acirExecutionResult = await simulator
|
|
860
|
+
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, buildACIRCallback(oracle))
|
|
753
861
|
.catch((err: Error) => {
|
|
754
862
|
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
755
863
|
throw new ExecutionError(
|
|
@@ -776,7 +884,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
776
884
|
}
|
|
777
885
|
|
|
778
886
|
private async getLastBlockNumber(): Promise<BlockNumber> {
|
|
779
|
-
const
|
|
780
|
-
return
|
|
887
|
+
const block = await this.stateMachine.node.getBlock('latest');
|
|
888
|
+
return block ? block.header.globalVariables.blockNumber : BlockNumber.ZERO;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
private buildAuthorizeUtilityCallHook(
|
|
892
|
+
callerContext: 'private' | 'private view' | 'utility',
|
|
893
|
+
authorizedTargets: AztecAddress[],
|
|
894
|
+
): ExecutionHooks['authorizeUtilityCall'] | undefined {
|
|
895
|
+
if (authorizedTargets.length === 0) {
|
|
896
|
+
return undefined;
|
|
897
|
+
}
|
|
898
|
+
return req =>
|
|
899
|
+
Promise.resolve({
|
|
900
|
+
authorized: req.callerContext === callerContext && authorizedTargets.some(t => t.equals(req.target)),
|
|
901
|
+
});
|
|
781
902
|
}
|
|
782
903
|
}
|
|
@@ -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
|
+
}
|