@aztec/txe 5.0.0-private.20260319 → 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 +33 -20
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +151 -58
- 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 -233
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +695 -636
- 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 +1 -2
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +0 -3
- package/dest/state_machine/synchronizer.js +1 -1
- package/dest/txe_session.d.ts +86 -19
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +244 -45
- 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 -67
- 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 +765 -913
- 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 +0 -4
- package/src/state_machine/synchronizer.ts +1 -1
- package/src/txe_session.ts +434 -57
- 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,18 +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,
|
|
20
|
-
type
|
|
17
|
+
type ExecutionHooks,
|
|
21
18
|
NoteStore,
|
|
22
|
-
|
|
19
|
+
ORACLE_VERSION_MAJOR,
|
|
23
20
|
PrivateEventStore,
|
|
24
21
|
RecipientTaggingStore,
|
|
25
22
|
SenderAddressBookStore,
|
|
26
23
|
SenderTaggingStore,
|
|
24
|
+
composeHooks,
|
|
27
25
|
enrichPublicSimulationError,
|
|
28
26
|
} from '@aztec/pxe/server';
|
|
29
27
|
import {
|
|
@@ -31,9 +29,10 @@ import {
|
|
|
31
29
|
ExecutionTaggingIndexCache,
|
|
32
30
|
HashedValuesCache,
|
|
33
31
|
type IMiscOracle,
|
|
34
|
-
Oracle,
|
|
35
32
|
PrivateExecutionOracle,
|
|
33
|
+
TransientArrayService,
|
|
36
34
|
UtilityExecutionOracle,
|
|
35
|
+
buildACIRCallback,
|
|
37
36
|
executePrivateFunction,
|
|
38
37
|
generateSimulatedProvingResult,
|
|
39
38
|
} from '@aztec/pxe/simulator';
|
|
@@ -65,6 +64,7 @@ import {
|
|
|
65
64
|
PrivateToPublicAccumulatedData,
|
|
66
65
|
PublicCallRequest,
|
|
67
66
|
} from '@aztec/stdlib/kernel';
|
|
67
|
+
import { hashPublicKey } from '@aztec/stdlib/keys';
|
|
68
68
|
import { ChonkProof } from '@aztec/stdlib/proofs';
|
|
69
69
|
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
70
70
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
@@ -81,13 +81,14 @@ import {
|
|
|
81
81
|
collectNested,
|
|
82
82
|
} from '@aztec/stdlib/tx';
|
|
83
83
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
84
|
-
import { ForkCheckpoint } from '@aztec/world-state';
|
|
84
|
+
import { ForkCheckpoint } from '@aztec/world-state/native';
|
|
85
85
|
|
|
86
|
-
import { DEFAULT_ADDRESS } from '../constants.js';
|
|
86
|
+
import { DEFAULT_ADDRESS, MAX_PRIVATE_EVENTS_PER_TXE_QUERY, MAX_PRIVATE_EVENT_LEN } from '../constants.js';
|
|
87
87
|
import type { TXEStateMachine } from '../state_machine/index.js';
|
|
88
|
-
import type { TXEAccountStore } from '../util/txe_account_store.js';
|
|
89
|
-
import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
|
|
90
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';
|
|
91
92
|
import type { ITxeExecutionOracle } from './interfaces.js';
|
|
92
93
|
|
|
93
94
|
export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle {
|
|
@@ -112,18 +113,32 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
112
113
|
private version: Fr,
|
|
113
114
|
private chainId: Fr,
|
|
114
115
|
private authwits: Map<string, AuthWitness>,
|
|
115
|
-
private readonly
|
|
116
|
+
private readonly artifactResolver: TXEArtifactResolver,
|
|
117
|
+
private readonly rootPath: string,
|
|
118
|
+
private readonly packageName: string,
|
|
116
119
|
) {
|
|
117
120
|
this.logger = createLogger('txe:top_level_context');
|
|
118
121
|
this.logger.debug('Entering Top Level Context');
|
|
119
122
|
}
|
|
120
123
|
|
|
121
|
-
|
|
122
|
-
|
|
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.';
|
|
123
132
|
throw new Error(
|
|
124
|
-
`Incompatible
|
|
133
|
+
`Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`,
|
|
125
134
|
);
|
|
126
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;
|
|
127
142
|
}
|
|
128
143
|
|
|
129
144
|
// This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
|
|
@@ -133,7 +148,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
133
148
|
}
|
|
134
149
|
|
|
135
150
|
// We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
|
|
136
|
-
log(level: number, message: string, fields: Fr[]): Promise<void> {
|
|
151
|
+
log(level: number, message: string, _fieldsSize: number, fields: Fr[]): Promise<void> {
|
|
137
152
|
if (!LogLevels[level]) {
|
|
138
153
|
throw new Error(`Invalid log level: ${level}`);
|
|
139
154
|
}
|
|
@@ -156,12 +171,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
156
171
|
}
|
|
157
172
|
|
|
158
173
|
async getLastBlockTimestamp() {
|
|
159
|
-
return (await this.stateMachine.node.
|
|
174
|
+
return (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables.timestamp;
|
|
160
175
|
}
|
|
161
176
|
|
|
162
|
-
async getLastTxEffects() {
|
|
177
|
+
async getLastTxEffects(): ReturnType<ITxeExecutionOracle['getLastTxEffects']> {
|
|
163
178
|
const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
|
|
164
|
-
const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
|
|
179
|
+
const block = await this.stateMachine.archiver.getBlock({ number: latestBlockNumber });
|
|
165
180
|
|
|
166
181
|
if (block!.body.txEffects.length != 1) {
|
|
167
182
|
// Note that calls like env.mine() will result in blocks with no transactions, hitting this
|
|
@@ -170,7 +185,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
170
185
|
|
|
171
186
|
const txEffects = block!.body.txEffects[0];
|
|
172
187
|
|
|
173
|
-
|
|
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
|
+
};
|
|
174
199
|
}
|
|
175
200
|
|
|
176
201
|
async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
|
|
@@ -193,7 +218,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
193
218
|
}
|
|
194
219
|
|
|
195
220
|
async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
|
|
196
|
-
|
|
221
|
+
const events = (
|
|
197
222
|
await this.privateEventStore.getPrivateEvents(selector, {
|
|
198
223
|
contractAddress,
|
|
199
224
|
scopes: [scope],
|
|
@@ -201,6 +226,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
201
226
|
toBlock: (await this.getLastBlockNumber()) + 1,
|
|
202
227
|
})
|
|
203
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;
|
|
204
238
|
}
|
|
205
239
|
|
|
206
240
|
async advanceBlocksBy(blocks: number) {
|
|
@@ -216,27 +250,71 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
216
250
|
this.nextBlockTimestamp += duration;
|
|
217
251
|
}
|
|
218
252
|
|
|
219
|
-
|
|
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
|
+
|
|
220
276
|
// Emit deployment nullifier
|
|
221
277
|
await this.mineBlock({
|
|
222
|
-
nullifiers: [
|
|
223
|
-
await siloNullifier(
|
|
224
|
-
AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
|
|
225
|
-
instance.address.toField(),
|
|
226
|
-
),
|
|
227
|
-
],
|
|
278
|
+
nullifiers: [await this.deploymentNullifier(instance.address)],
|
|
228
279
|
});
|
|
229
280
|
|
|
230
281
|
if (!secret.equals(Fr.ZERO)) {
|
|
231
|
-
await this.
|
|
282
|
+
await this.registerContractAndAddAccount(artifact, instance, secret);
|
|
232
283
|
} else {
|
|
233
284
|
await this.contractStore.addContractInstance(instance);
|
|
234
285
|
await this.contractStore.addContractArtifact(artifact);
|
|
235
286
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
236
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);
|
|
237
311
|
}
|
|
238
312
|
|
|
239
|
-
async
|
|
313
|
+
private async registerContractAndAddAccount(
|
|
314
|
+
artifact: ContractArtifact,
|
|
315
|
+
instance: ContractInstanceWithAddress,
|
|
316
|
+
secret: Fr,
|
|
317
|
+
) {
|
|
240
318
|
const partialAddress = await computePartialAddress(instance);
|
|
241
319
|
|
|
242
320
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
@@ -263,12 +341,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
263
341
|
|
|
264
342
|
async addAuthWitness(address: AztecAddress, messageHash: Fr) {
|
|
265
343
|
const account = await this.accountStore.getAccount(address);
|
|
266
|
-
const
|
|
344
|
+
const ivpkMHash = await hashPublicKey(account.publicKeys.ivpkM);
|
|
345
|
+
const privateKey = await this.keyStore.getMasterSecretKey(ivpkMHash);
|
|
267
346
|
|
|
268
347
|
const schnorr = new Schnorr();
|
|
269
|
-
const signature = await schnorr.constructSignature(messageHash
|
|
348
|
+
const signature = await schnorr.constructSignature(messageHash, privateKey);
|
|
270
349
|
|
|
271
|
-
const authWitness = new AuthWitness(messageHash,
|
|
350
|
+
const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
|
|
272
351
|
|
|
273
352
|
this.authwits.set(authWitness.requestHash.toString(), authWitness);
|
|
274
353
|
}
|
|
@@ -299,13 +378,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
299
378
|
}
|
|
300
379
|
|
|
301
380
|
async privateCallNewFlow(
|
|
302
|
-
from: AztecAddress,
|
|
381
|
+
from: AztecAddress | undefined,
|
|
303
382
|
targetContractAddress: AztecAddress = AztecAddress.zero(),
|
|
304
383
|
functionSelector: FunctionSelector = FunctionSelector.empty(),
|
|
305
384
|
args: Fr[],
|
|
306
385
|
argsHash: Fr = Fr.zero(),
|
|
307
386
|
isStaticCall: boolean = false,
|
|
387
|
+
additionalScopes: AztecAddress[] = [],
|
|
308
388
|
jobId: string,
|
|
389
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
390
|
+
gasSettings: GasSettings,
|
|
309
391
|
) {
|
|
310
392
|
this.logger.verbose(
|
|
311
393
|
`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
@@ -319,12 +401,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
319
401
|
throw new Error(message);
|
|
320
402
|
}
|
|
321
403
|
|
|
322
|
-
|
|
323
|
-
// empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
|
|
324
|
-
const effectiveScopes = from.isZero() ? [] : [from];
|
|
404
|
+
const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
|
|
325
405
|
|
|
326
406
|
// Sync notes before executing private function to discover notes from previous transactions
|
|
327
|
-
const utilityExecutor = async (call: FunctionCall, execScopes:
|
|
407
|
+
const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
|
|
328
408
|
await this.executeUtilityCall(call, execScopes, jobId);
|
|
329
409
|
};
|
|
330
410
|
|
|
@@ -335,16 +415,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
335
415
|
utilityExecutor,
|
|
336
416
|
blockHeader,
|
|
337
417
|
jobId,
|
|
338
|
-
|
|
418
|
+
scopes,
|
|
339
419
|
);
|
|
340
420
|
|
|
341
421
|
const blockNumber = await this.getNextBlockNumber();
|
|
342
422
|
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
346
|
-
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
347
|
-
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);
|
|
348
425
|
|
|
349
426
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
350
427
|
|
|
@@ -359,6 +436,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
359
436
|
|
|
360
437
|
const simulator = new WASMSimulator();
|
|
361
438
|
|
|
439
|
+
const transientArrayService = new TransientArrayService();
|
|
362
440
|
const privateExecutionOracle = new PrivateExecutionOracle({
|
|
363
441
|
argsHash,
|
|
364
442
|
txContext,
|
|
@@ -378,18 +456,26 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
378
456
|
senderTaggingStore: this.senderTaggingStore,
|
|
379
457
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
380
458
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
381
|
-
|
|
459
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
382
460
|
privateEventStore: this.privateEventStore,
|
|
383
461
|
contractSyncService: this.stateMachine.contractSyncService,
|
|
384
462
|
jobId,
|
|
385
463
|
totalPublicCalldataCount: 0,
|
|
386
464
|
sideEffectCounter: minRevertibleSideEffectCounter,
|
|
387
|
-
scopes
|
|
465
|
+
scopes,
|
|
388
466
|
// In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
389
467
|
// contract would perform, including setting senderForTags.
|
|
390
468
|
senderForTags: from,
|
|
391
469
|
simulator,
|
|
392
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,
|
|
393
479
|
});
|
|
394
480
|
|
|
395
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.
|
|
@@ -412,7 +498,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
412
498
|
);
|
|
413
499
|
const publicFunctionsCalldata = await Promise.all(
|
|
414
500
|
publicCallRequests.map(async r => {
|
|
415
|
-
const calldata = await privateExecutionOracle.
|
|
501
|
+
const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
|
|
416
502
|
return new HashedValues(calldata, r.calldataHash);
|
|
417
503
|
}),
|
|
418
504
|
);
|
|
@@ -497,11 +583,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
497
583
|
}
|
|
498
584
|
}
|
|
499
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
|
+
|
|
500
592
|
if (isStaticCall) {
|
|
501
593
|
await checkpoint!.revert();
|
|
502
594
|
|
|
503
595
|
await forkedWorldTrees.close();
|
|
504
|
-
return executionResult.returnValues ?? [];
|
|
596
|
+
return { returnValues: executionResult.returnValues ?? [], offchainEffects };
|
|
505
597
|
}
|
|
506
598
|
|
|
507
599
|
const txEffect = TxEffect.empty();
|
|
@@ -523,14 +615,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
523
615
|
|
|
524
616
|
await forkedWorldTrees.close();
|
|
525
617
|
|
|
526
|
-
return executionResult.returnValues ?? [];
|
|
618
|
+
return { returnValues: executionResult.returnValues ?? [], offchainEffects };
|
|
527
619
|
}
|
|
528
620
|
|
|
529
621
|
async publicCallNewFlow(
|
|
530
|
-
from: AztecAddress,
|
|
622
|
+
from: AztecAddress | undefined,
|
|
531
623
|
targetContractAddress: AztecAddress,
|
|
532
624
|
calldata: Fr[],
|
|
533
625
|
isStaticCall: boolean,
|
|
626
|
+
gasSettings: GasSettings,
|
|
534
627
|
) {
|
|
535
628
|
this.logger.verbose(
|
|
536
629
|
`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
@@ -538,12 +631,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
538
631
|
|
|
539
632
|
const blockNumber = await this.getNextBlockNumber();
|
|
540
633
|
|
|
541
|
-
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
542
|
-
|
|
543
|
-
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
544
|
-
|
|
545
|
-
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
546
|
-
|
|
547
634
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
548
635
|
|
|
549
636
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
@@ -595,7 +682,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
595
682
|
// may require producing reverts.
|
|
596
683
|
const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
597
684
|
revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
|
|
598
|
-
from,
|
|
685
|
+
from ?? AztecAddress.NULL_MSG_SENDER,
|
|
599
686
|
targetContractAddress,
|
|
600
687
|
isStaticCall,
|
|
601
688
|
calldataHash,
|
|
@@ -686,6 +773,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
686
773
|
functionSelector: FunctionSelector,
|
|
687
774
|
args: Fr[],
|
|
688
775
|
jobId: string,
|
|
776
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
689
777
|
) {
|
|
690
778
|
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
691
779
|
if (!artifact) {
|
|
@@ -702,7 +790,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
702
790
|
},
|
|
703
791
|
blockHeader,
|
|
704
792
|
jobId,
|
|
705
|
-
|
|
793
|
+
await this.keyStore.getAccounts(),
|
|
706
794
|
);
|
|
707
795
|
|
|
708
796
|
const call = FunctionCall.from({
|
|
@@ -716,10 +804,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
716
804
|
returnTypes: [],
|
|
717
805
|
});
|
|
718
806
|
|
|
719
|
-
return this.executeUtilityCall(call,
|
|
807
|
+
return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
|
|
720
808
|
}
|
|
721
809
|
|
|
722
|
-
private async executeUtilityCall(
|
|
810
|
+
private async executeUtilityCall(
|
|
811
|
+
call: FunctionCall,
|
|
812
|
+
scopes: AztecAddress[],
|
|
813
|
+
jobId: string,
|
|
814
|
+
authorizedUtilityCallTargets: AztecAddress[] = [],
|
|
815
|
+
): Promise<Fr[]> {
|
|
723
816
|
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
724
817
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
725
818
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
@@ -732,6 +825,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
732
825
|
|
|
733
826
|
try {
|
|
734
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
|
+
};
|
|
735
832
|
const oracle = new UtilityExecutionOracle({
|
|
736
833
|
contractAddress: call.to,
|
|
737
834
|
authWitnesses: [],
|
|
@@ -744,15 +841,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
744
841
|
aztecNode: this.stateMachine.node,
|
|
745
842
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
746
843
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
747
|
-
|
|
844
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
748
845
|
privateEventStore: this.privateEventStore,
|
|
749
846
|
messageContextService: this.stateMachine.messageContextService,
|
|
750
|
-
contractSyncService: this.contractSyncService,
|
|
847
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
848
|
+
l2TipsStore: this.stateMachine.l2TipsProvider,
|
|
751
849
|
jobId,
|
|
752
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(),
|
|
753
858
|
});
|
|
754
|
-
const acirExecutionResult = await
|
|
755
|
-
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact,
|
|
859
|
+
const acirExecutionResult = await simulator
|
|
860
|
+
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, buildACIRCallback(oracle))
|
|
756
861
|
.catch((err: Error) => {
|
|
757
862
|
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
758
863
|
throw new ExecutionError(
|
|
@@ -779,7 +884,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
779
884
|
}
|
|
780
885
|
|
|
781
886
|
private async getLastBlockNumber(): Promise<BlockNumber> {
|
|
782
|
-
const
|
|
783
|
-
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
|
+
});
|
|
784
902
|
}
|
|
785
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
|
+
}
|