@aztec/txe 0.0.1-commit.733c4a3 → 0.0.1-commit.7689c7f
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-KKNT527R.js +3 -0
- package/dest/ContractClassRegistry-KKNT527R.js.map +7 -0
- package/dest/ContractInstanceRegistry-GSKBNL3Q.js +3 -0
- package/dest/ContractInstanceRegistry-GSKBNL3Q.js.map +7 -0
- package/dest/FeeJuice-GUJ5O6GB.js +3 -0
- package/dest/FeeJuice-GUJ5O6GB.js.map +7 -0
- package/dest/MultiCallEntrypoint-ZQPCKVKL.js +3 -0
- package/dest/MultiCallEntrypoint-ZQPCKVKL.js.map +7 -0
- package/dest/SchnorrAccount-QIXE5DXA.js +3 -0
- package/dest/SchnorrAccount-QIXE5DXA.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 +32 -0
- package/dest/bin/index.js +3 -30
- package/dest/bin/index.js.map +7 -0
- package/dest/chunk-BJVAAXNA.js +3 -0
- package/dest/chunk-BJVAAXNA.js.map +7 -0
- package/dest/chunk-DF6RMOC6.js +304 -0
- package/dest/chunk-DF6RMOC6.js.map +7 -0
- package/dest/chunk-PPJTNNUG.js +265 -0
- package/dest/chunk-PPJTNNUG.js.map +7 -0
- package/dest/constants.d.ts +3 -1
- package/dest/constants.d.ts.map +1 -1
- package/dest/constants.js +5 -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 +188 -7
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +160 -119
- package/dest/metafile.json +38147 -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 +39 -28
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.d.ts +18 -13
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +27 -12
- package/dest/oracle/txe_oracle_top_level_context.d.ts +36 -20
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +125 -66
- 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 +139 -83
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +568 -244
- package/dest/server.bundle.js +3 -0
- package/dest/server.bundle.js.map +7 -0
- package/dest/state_machine/archiver.d.ts +4 -4
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +22 -20
- 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 +7 -3
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +23 -5
- package/dest/state_machine/mock_epoch_cache.d.ts +18 -3
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +35 -2
- package/dest/state_machine/synchronizer.d.ts +5 -5
- package/dest/state_machine/synchronizer.d.ts.map +1 -1
- package/dest/state_machine/synchronizer.js +4 -4
- package/dest/txe_oracle_version.d.ts +17 -0
- package/dest/txe_oracle_version.d.ts.map +1 -0
- package/dest/txe_oracle_version.js +14 -0
- package/dest/txe_session.d.ts +64 -6
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +159 -31
- 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_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 +37 -21
- package/src/bin/check_txe_oracle_version.ts +37 -0
- package/src/bin/index.ts +11 -2
- package/src/constants.ts +6 -0
- package/src/dispatcher_pool.ts +316 -0
- package/src/index.ts +178 -126
- package/src/msgpackr_fr_extension.ts +23 -0
- package/src/oracle/interfaces.ts +43 -34
- package/src/oracle/txe_oracle_public_context.ts +42 -12
- package/src/oracle/txe_oracle_top_level_context.ts +146 -77
- package/src/oracle/txe_private_execution_oracle.ts +30 -0
- package/src/rpc_server.ts +87 -0
- package/src/rpc_translator.ts +647 -327
- package/src/state_machine/archiver.ts +33 -18
- 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 +25 -6
- package/src/state_machine/mock_epoch_cache.ts +46 -3
- package/src/state_machine/synchronizer.ts +5 -5
- package/src/txe_oracle_version.ts +17 -0
- package/src/txe_session.ts +253 -33
- package/src/{util → utils}/encoding.ts +8 -2
- 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,10 +1,10 @@
|
|
|
1
|
-
import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
|
|
1
|
+
import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, MAX_PRIVATE_LOGS_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
2
2
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
3
|
import { Schnorr } from '@aztec/foundation/crypto/schnorr';
|
|
4
4
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
5
|
import { LogLevels, applyStringFormatting, createLogger } from '@aztec/foundation/log';
|
|
6
6
|
import { TestDateProvider } from '@aztec/foundation/timer';
|
|
7
|
-
import {
|
|
7
|
+
import { CapsuleService, ORACLE_VERSION_MAJOR, composeHooks, enrichPublicSimulationError } from '@aztec/pxe/server';
|
|
8
8
|
import { ExecutionNoteCache, ExecutionTaggingIndexCache, HashedValuesCache, Oracle, PrivateExecutionOracle, UtilityExecutionOracle, executePrivateFunction, generateSimulatedProvingResult } from '@aztec/pxe/simulator';
|
|
9
9
|
import { ExecutionError, WASMSimulator, createSimulationError, extractCallStack, resolveAssertionMessageFromError, toACVMWitness, witnessMapToFields } from '@aztec/simulator/client';
|
|
10
10
|
import { CppPublicTxSimulator, GuardedMerkleTreeOperations, PublicContractsDB, PublicProcessor } from '@aztec/simulator/server';
|
|
@@ -13,17 +13,18 @@ import { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
|
13
13
|
import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
14
14
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
15
15
|
import { computePartialAddress } from '@aztec/stdlib/contract';
|
|
16
|
-
import { Gas, GasFees
|
|
16
|
+
import { Gas, GasFees } from '@aztec/stdlib/gas';
|
|
17
17
|
import { computeCalldataHash, computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
|
|
18
18
|
import { PartialPrivateTailPublicInputsForPublic, PrivateKernelTailCircuitPublicInputs, PrivateToPublicAccumulatedData, PublicCallRequest } from '@aztec/stdlib/kernel';
|
|
19
|
+
import { hashPublicKey } from '@aztec/stdlib/keys';
|
|
19
20
|
import { ChonkProof } from '@aztec/stdlib/proofs';
|
|
20
21
|
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
21
22
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
22
23
|
import { CallContext, HashedValues, PrivateExecutionResult, Tx, TxConstantData, TxContext, TxEffect, TxHash, collectNested } from '@aztec/stdlib/tx';
|
|
23
|
-
import { ForkCheckpoint } from '@aztec/world-state';
|
|
24
|
+
import { ForkCheckpoint } from '@aztec/world-state/native';
|
|
24
25
|
import { DEFAULT_ADDRESS } from '../constants.js';
|
|
25
|
-
import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
|
|
26
26
|
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
|
|
27
|
+
import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js';
|
|
27
28
|
export class TXEOracleTopLevelContext {
|
|
28
29
|
stateMachine;
|
|
29
30
|
contractStore;
|
|
@@ -64,18 +65,28 @@ export class TXEOracleTopLevelContext {
|
|
|
64
65
|
this.logger = createLogger('txe:top_level_context');
|
|
65
66
|
this.logger.debug('Entering Top Level Context');
|
|
66
67
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
contractOracleVersion;
|
|
69
|
+
assertCompatibleOracleVersion(major, minor) {
|
|
70
|
+
if (major !== ORACLE_VERSION_MAJOR) {
|
|
71
|
+
const hint = major > ORACLE_VERSION_MAJOR ? 'The contract was compiled with a newer version of Aztec.nr than this aztec cli version supports. Upgrade your aztec cli version to a compatible version.' : 'The contract was compiled with an older version of Aztec.nr than this aztec cli version supports. Recompile the contract with a compatible version of Aztec.nr.';
|
|
72
|
+
throw new Error(`Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`);
|
|
70
73
|
}
|
|
74
|
+
this.contractOracleVersion = {
|
|
75
|
+
major,
|
|
76
|
+
minor
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
|
|
80
|
+
nonOracleFunctionGetContractOracleVersion() {
|
|
81
|
+
return this.contractOracleVersion;
|
|
71
82
|
}
|
|
72
83
|
// This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
|
|
73
84
|
// setup.
|
|
74
|
-
|
|
85
|
+
getRandomField() {
|
|
75
86
|
return Fr.random();
|
|
76
87
|
}
|
|
77
88
|
// We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
|
|
78
|
-
|
|
89
|
+
log(level, message, fields) {
|
|
79
90
|
if (!LogLevels[level]) {
|
|
80
91
|
throw new Error(`Invalid log level: ${level}`);
|
|
81
92
|
}
|
|
@@ -85,35 +96,42 @@ export class TXEOracleTopLevelContext {
|
|
|
85
96
|
});
|
|
86
97
|
return Promise.resolve();
|
|
87
98
|
}
|
|
88
|
-
|
|
99
|
+
getDefaultAddress() {
|
|
89
100
|
return DEFAULT_ADDRESS;
|
|
90
101
|
}
|
|
91
|
-
async
|
|
102
|
+
async getNextBlockNumber() {
|
|
92
103
|
return BlockNumber(await this.getLastBlockNumber() + 1);
|
|
93
104
|
}
|
|
94
|
-
|
|
105
|
+
getNextBlockTimestamp() {
|
|
95
106
|
return Promise.resolve(this.nextBlockTimestamp);
|
|
96
107
|
}
|
|
97
|
-
async
|
|
98
|
-
return (await this.stateMachine.node.
|
|
108
|
+
async getLastBlockTimestamp() {
|
|
109
|
+
return (await this.stateMachine.node.getBlockData('latest')).header.globalVariables.timestamp;
|
|
99
110
|
}
|
|
100
|
-
async
|
|
111
|
+
async getLastTxEffects() {
|
|
101
112
|
const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
|
|
102
|
-
const block = await this.stateMachine.archiver.getBlock(
|
|
113
|
+
const block = await this.stateMachine.archiver.getBlock({
|
|
114
|
+
number: latestBlockNumber
|
|
115
|
+
});
|
|
103
116
|
if (block.body.txEffects.length != 1) {
|
|
104
117
|
// Note that calls like env.mine() will result in blocks with no transactions, hitting this
|
|
105
118
|
throw new Error(`Expected a single transaction in the last block, found ${block.body.txEffects.length}`);
|
|
106
119
|
}
|
|
107
120
|
const txEffects = block.body.txEffects[0];
|
|
121
|
+
const privateLogs = txEffects.privateLogs;
|
|
122
|
+
if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
|
|
123
|
+
throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
|
|
124
|
+
}
|
|
108
125
|
return {
|
|
109
126
|
txHash: txEffects.txHash,
|
|
110
127
|
noteHashes: txEffects.noteHashes,
|
|
111
|
-
nullifiers: txEffects.nullifiers
|
|
128
|
+
nullifiers: txEffects.nullifiers,
|
|
129
|
+
privateLogs
|
|
112
130
|
};
|
|
113
131
|
}
|
|
114
132
|
async syncContractNonOracleMethod(contractAddress, scope, jobId) {
|
|
115
133
|
if (contractAddress.equals(DEFAULT_ADDRESS)) {
|
|
116
|
-
this.logger.debug(`Skipping sync in
|
|
134
|
+
this.logger.debug(`Skipping sync in getPrivateEvents because the events correspond to the default address.`);
|
|
117
135
|
return;
|
|
118
136
|
}
|
|
119
137
|
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
@@ -123,7 +141,7 @@ export class TXEOracleTopLevelContext {
|
|
|
123
141
|
scope
|
|
124
142
|
]);
|
|
125
143
|
}
|
|
126
|
-
async
|
|
144
|
+
async getPrivateEvents(selector, contractAddress, scope) {
|
|
127
145
|
return (await this.privateEventStore.getPrivateEvents(selector, {
|
|
128
146
|
contractAddress,
|
|
129
147
|
scopes: [
|
|
@@ -133,32 +151,42 @@ export class TXEOracleTopLevelContext {
|
|
|
133
151
|
toBlock: await this.getLastBlockNumber() + 1
|
|
134
152
|
})).map((e)=>e.packedEvent);
|
|
135
153
|
}
|
|
136
|
-
async
|
|
154
|
+
async advanceBlocksBy(blocks) {
|
|
137
155
|
this.logger.debug(`time traveling ${blocks} blocks`);
|
|
138
156
|
for(let i = 0; i < blocks; i++){
|
|
139
157
|
await this.mineBlock();
|
|
140
158
|
}
|
|
141
159
|
}
|
|
142
|
-
|
|
160
|
+
advanceTimestampBy(duration) {
|
|
143
161
|
this.logger.debug(`time traveling ${duration} seconds`);
|
|
144
162
|
this.nextBlockTimestamp += duration;
|
|
145
163
|
}
|
|
146
|
-
|
|
164
|
+
deploymentNullifier(address) {
|
|
165
|
+
return siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address.toField());
|
|
166
|
+
}
|
|
167
|
+
async deploy(artifact, instance, secret) {
|
|
147
168
|
// Emit deployment nullifier
|
|
148
169
|
await this.mineBlock({
|
|
149
170
|
nullifiers: [
|
|
150
|
-
await
|
|
171
|
+
await this.deploymentNullifier(instance.address)
|
|
151
172
|
]
|
|
152
173
|
});
|
|
153
174
|
if (!secret.equals(Fr.ZERO)) {
|
|
154
|
-
await this.
|
|
175
|
+
await this.addAccount(artifact, instance, secret);
|
|
155
176
|
} else {
|
|
156
177
|
await this.contractStore.addContractInstance(instance);
|
|
157
178
|
await this.contractStore.addContractArtifact(artifact);
|
|
158
179
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
159
180
|
}
|
|
160
181
|
}
|
|
161
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Mines a single block containing only the deployment nullifiers for the contracts at the given addresses.
|
|
184
|
+
*/ async mineDeploymentNullifiers(addresses) {
|
|
185
|
+
await this.mineBlock({
|
|
186
|
+
nullifiers: await Promise.all(addresses.map((address)=>this.deploymentNullifier(address)))
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
async addAccount(artifact, instance, secret) {
|
|
162
190
|
const partialAddress = await computePartialAddress(instance);
|
|
163
191
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
164
192
|
await this.contractStore.addContractInstance(instance);
|
|
@@ -169,7 +197,7 @@ export class TXEOracleTopLevelContext {
|
|
|
169
197
|
this.logger.debug(`Created account ${completeAddress.address}`);
|
|
170
198
|
return completeAddress;
|
|
171
199
|
}
|
|
172
|
-
async
|
|
200
|
+
async createAccount(secret) {
|
|
173
201
|
// This is a foot gun !
|
|
174
202
|
const completeAddress = await this.keyStore.addAccount(secret, secret);
|
|
175
203
|
await this.accountStore.setAccount(completeAddress.address, completeAddress);
|
|
@@ -177,18 +205,17 @@ export class TXEOracleTopLevelContext {
|
|
|
177
205
|
this.logger.debug(`Created account ${completeAddress.address}`);
|
|
178
206
|
return completeAddress;
|
|
179
207
|
}
|
|
180
|
-
async
|
|
208
|
+
async addAuthWitness(address, messageHash) {
|
|
181
209
|
const account = await this.accountStore.getAccount(address);
|
|
182
|
-
const
|
|
210
|
+
const ivpkMHash = await hashPublicKey(account.publicKeys.ivpkM);
|
|
211
|
+
const privateKey = await this.keyStore.getMasterSecretKey(ivpkMHash);
|
|
183
212
|
const schnorr = new Schnorr();
|
|
184
|
-
const signature = await schnorr.constructSignature(messageHash
|
|
185
|
-
const authWitness = new AuthWitness(messageHash,
|
|
186
|
-
...signature.toBuffer()
|
|
187
|
-
]);
|
|
213
|
+
const signature = await schnorr.constructSignature(messageHash, privateKey);
|
|
214
|
+
const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
|
|
188
215
|
this.authwits.set(authWitness.requestHash.toString(), authWitness);
|
|
189
216
|
}
|
|
190
217
|
async mineBlock(options = {}) {
|
|
191
|
-
const blockNumber = await this.
|
|
218
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
192
219
|
const txEffect = TxEffect.empty();
|
|
193
220
|
txEffect.nullifiers = [
|
|
194
221
|
getSingleTxBlockRequestHash(blockNumber),
|
|
@@ -210,29 +237,26 @@ export class TXEOracleTopLevelContext {
|
|
|
210
237
|
this.logger.info(`Created block ${blockNumber} with timestamp ${block.header.globalVariables.timestamp}`);
|
|
211
238
|
await this.stateMachine.handleL2Block(block);
|
|
212
239
|
}
|
|
213
|
-
async
|
|
240
|
+
async privateCallNewFlow(from, targetContractAddress = AztecAddress.zero(), functionSelector = FunctionSelector.empty(), args, argsHash = Fr.zero(), isStaticCall = false, additionalScopes = [], jobId, authorizedUtilityCallTargets, gasSettings) {
|
|
214
241
|
this.logger.verbose(`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`);
|
|
215
242
|
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
216
243
|
if (!artifact) {
|
|
217
244
|
const message = functionSelector.equals(await FunctionSelector.fromSignature('verify_private_authwit(Field)')) ? 'Found no account contract artifact for a private authwit check - use `create_contract_account` instead of `create_light_account` for authwit support.' : 'Function Artifact does not exist';
|
|
218
245
|
throw new Error(message);
|
|
219
246
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
from
|
|
247
|
+
const scopes = from === undefined ? additionalScopes : [
|
|
248
|
+
from,
|
|
249
|
+
...additionalScopes
|
|
224
250
|
];
|
|
225
251
|
// Sync notes before executing private function to discover notes from previous transactions
|
|
226
252
|
const utilityExecutor = async (call, execScopes)=>{
|
|
227
253
|
await this.executeUtilityCall(call, execScopes, jobId);
|
|
228
254
|
};
|
|
229
255
|
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
230
|
-
await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, utilityExecutor, blockHeader, jobId,
|
|
231
|
-
const blockNumber = await this.
|
|
232
|
-
const
|
|
233
|
-
const
|
|
234
|
-
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
235
|
-
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
256
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, utilityExecutor, blockHeader, jobId, scopes);
|
|
257
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
258
|
+
const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
|
|
259
|
+
const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
|
|
236
260
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
237
261
|
const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
|
|
238
262
|
const noteCache = new ExecutionNoteCache(protocolNullifier);
|
|
@@ -264,17 +288,22 @@ export class TXEOracleTopLevelContext {
|
|
|
264
288
|
senderTaggingStore: this.senderTaggingStore,
|
|
265
289
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
266
290
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
267
|
-
|
|
291
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
268
292
|
privateEventStore: this.privateEventStore,
|
|
269
293
|
contractSyncService: this.stateMachine.contractSyncService,
|
|
270
294
|
jobId,
|
|
271
295
|
totalPublicCalldataCount: 0,
|
|
272
296
|
sideEffectCounter: minRevertibleSideEffectCounter,
|
|
273
|
-
scopes
|
|
297
|
+
scopes,
|
|
274
298
|
// In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
275
299
|
// contract would perform, including setting senderForTags.
|
|
276
300
|
senderForTags: from,
|
|
277
|
-
simulator
|
|
301
|
+
simulator,
|
|
302
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
303
|
+
l2TipsStore: this.stateMachine.l2TipsProvider,
|
|
304
|
+
hooks: composeHooks({
|
|
305
|
+
authorizeUtilityCall: this.buildAuthorizeUtilityCallHook(isStaticCall ? 'private view' : 'private', authorizedUtilityCallTargets)
|
|
306
|
+
})
|
|
278
307
|
});
|
|
279
308
|
// Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks.
|
|
280
309
|
let result;
|
|
@@ -287,7 +316,7 @@ export class TXEOracleTopLevelContext {
|
|
|
287
316
|
r.publicInputs.publicTeardownCallRequest
|
|
288
317
|
]));
|
|
289
318
|
const publicFunctionsCalldata = await Promise.all(publicCallRequests.map(async (r)=>{
|
|
290
|
-
const calldata = await privateExecutionOracle.
|
|
319
|
+
const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
|
|
291
320
|
return new HashedValues(calldata, r.calldataHash);
|
|
292
321
|
}));
|
|
293
322
|
noteCache.finish();
|
|
@@ -346,10 +375,20 @@ export class TXEOracleTopLevelContext {
|
|
|
346
375
|
throw new Error('Contract execution has reverted');
|
|
347
376
|
}
|
|
348
377
|
}
|
|
378
|
+
// Walk the nested private-call tree and collect every offchain effect the transaction emitted.
|
|
379
|
+
// PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
|
|
380
|
+
// `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
|
|
381
|
+
// we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
|
|
382
|
+
const offchainEffects = collectNested([
|
|
383
|
+
executionResult
|
|
384
|
+
], (r)=>r.offchainEffects.map((e)=>e.data));
|
|
349
385
|
if (isStaticCall) {
|
|
350
386
|
await checkpoint.revert();
|
|
351
387
|
await forkedWorldTrees.close();
|
|
352
|
-
return
|
|
388
|
+
return {
|
|
389
|
+
returnValues: executionResult.returnValues ?? [],
|
|
390
|
+
offchainEffects
|
|
391
|
+
};
|
|
353
392
|
}
|
|
354
393
|
const txEffect = TxEffect.empty();
|
|
355
394
|
txEffect.noteHashes = processedTx.txEffect.noteHashes;
|
|
@@ -365,14 +404,14 @@ export class TXEOracleTopLevelContext {
|
|
|
365
404
|
]);
|
|
366
405
|
await this.stateMachine.handleL2Block(l2Block);
|
|
367
406
|
await forkedWorldTrees.close();
|
|
368
|
-
return
|
|
407
|
+
return {
|
|
408
|
+
returnValues: executionResult.returnValues ?? [],
|
|
409
|
+
offchainEffects
|
|
410
|
+
};
|
|
369
411
|
}
|
|
370
|
-
async
|
|
412
|
+
async publicCallNewFlow(from, targetContractAddress, calldata, isStaticCall, gasSettings) {
|
|
371
413
|
this.logger.verbose(`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`);
|
|
372
|
-
const blockNumber = await this.
|
|
373
|
-
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
374
|
-
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
375
|
-
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
414
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
376
415
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
377
416
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
378
417
|
const calldataHash = await computeCalldataHash(calldata);
|
|
@@ -405,7 +444,7 @@ export class TXEOracleTopLevelContext {
|
|
|
405
444
|
// The enqueued public call itself we make be revertible so that the public execution is itself revertible, as tests
|
|
406
445
|
// may require producing reverts.
|
|
407
446
|
const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
408
|
-
revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(from, targetContractAddress, isStaticCall, calldataHash);
|
|
447
|
+
revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(from ?? AztecAddress.NULL_MSG_SENDER, targetContractAddress, isStaticCall, calldataHash);
|
|
409
448
|
const inputsForPublic = new PartialPrivateTailPublicInputsForPublic(nonRevertibleAccumulatedData, revertibleAccumulatedData, PublicCallRequest.empty());
|
|
410
449
|
const constantData = new TxConstantData(anchorBlockHeader, txContext, Fr.zero(), Fr.zero());
|
|
411
450
|
const txData = new PrivateKernelTailCircuitPublicInputs(constantData, /*gasUsed=*/ new Gas(0, 0), /*feePayer=*/ AztecAddress.zero(), /*expirationTimestamp=*/ 0n, inputsForPublic, undefined);
|
|
@@ -461,7 +500,7 @@ export class TXEOracleTopLevelContext {
|
|
|
461
500
|
await forkedWorldTrees.close();
|
|
462
501
|
return returnValues ?? [];
|
|
463
502
|
}
|
|
464
|
-
async
|
|
503
|
+
async executeUtilityFunction(targetContractAddress, functionSelector, args, jobId, authorizedUtilityCallTargets) {
|
|
465
504
|
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
466
505
|
if (!artifact) {
|
|
467
506
|
throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
|
|
@@ -470,7 +509,7 @@ export class TXEOracleTopLevelContext {
|
|
|
470
509
|
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
471
510
|
await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, async (call, execScopes)=>{
|
|
472
511
|
await this.executeUtilityCall(call, execScopes, jobId);
|
|
473
|
-
}, blockHeader, jobId,
|
|
512
|
+
}, blockHeader, jobId, await this.keyStore.getAccounts());
|
|
474
513
|
const call = FunctionCall.from({
|
|
475
514
|
name: artifact.name,
|
|
476
515
|
to: targetContractAddress,
|
|
@@ -481,9 +520,9 @@ export class TXEOracleTopLevelContext {
|
|
|
481
520
|
args,
|
|
482
521
|
returnTypes: []
|
|
483
522
|
});
|
|
484
|
-
return this.executeUtilityCall(call,
|
|
523
|
+
return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
|
|
485
524
|
}
|
|
486
|
-
async executeUtilityCall(call, scopes, jobId) {
|
|
525
|
+
async executeUtilityCall(call, scopes, jobId, authorizedUtilityCallTargets = []) {
|
|
487
526
|
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
488
527
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
489
528
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
@@ -494,6 +533,10 @@ export class TXEOracleTopLevelContext {
|
|
|
494
533
|
});
|
|
495
534
|
try {
|
|
496
535
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
536
|
+
const simulator = new WASMSimulator();
|
|
537
|
+
const utilityExecutor = async (syncCall, execScopes)=>{
|
|
538
|
+
await this.executeUtilityCall(syncCall, execScopes, jobId);
|
|
539
|
+
};
|
|
497
540
|
const oracle = new UtilityExecutionOracle({
|
|
498
541
|
contractAddress: call.to,
|
|
499
542
|
authWitnesses: [],
|
|
@@ -506,12 +549,20 @@ export class TXEOracleTopLevelContext {
|
|
|
506
549
|
aztecNode: this.stateMachine.node,
|
|
507
550
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
508
551
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
509
|
-
|
|
552
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
510
553
|
privateEventStore: this.privateEventStore,
|
|
554
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
555
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
556
|
+
l2TipsStore: this.stateMachine.l2TipsProvider,
|
|
511
557
|
jobId,
|
|
512
|
-
scopes
|
|
558
|
+
scopes,
|
|
559
|
+
simulator,
|
|
560
|
+
hooks: composeHooks({
|
|
561
|
+
authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets)
|
|
562
|
+
}),
|
|
563
|
+
utilityExecutor
|
|
513
564
|
});
|
|
514
|
-
const acirExecutionResult = await
|
|
565
|
+
const acirExecutionResult = await simulator.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback()).catch((err)=>{
|
|
515
566
|
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
516
567
|
throw new ExecutionError(err.message, {
|
|
517
568
|
contractAddress: call.to,
|
|
@@ -534,7 +585,15 @@ export class TXEOracleTopLevelContext {
|
|
|
534
585
|
];
|
|
535
586
|
}
|
|
536
587
|
async getLastBlockNumber() {
|
|
537
|
-
const
|
|
538
|
-
return
|
|
588
|
+
const block = await this.stateMachine.node.getBlock('latest');
|
|
589
|
+
return block ? block.header.globalVariables.blockNumber : BlockNumber.ZERO;
|
|
590
|
+
}
|
|
591
|
+
buildAuthorizeUtilityCallHook(callerContext, authorizedTargets) {
|
|
592
|
+
if (authorizedTargets.length === 0) {
|
|
593
|
+
return undefined;
|
|
594
|
+
}
|
|
595
|
+
return (req)=>Promise.resolve({
|
|
596
|
+
authorized: req.callerContext === callerContext && authorizedTargets.some((t)=>t.equals(req.target))
|
|
597
|
+
});
|
|
539
598
|
}
|
|
540
599
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
+
import { PrivateExecutionOracle } from '@aztec/pxe/simulator';
|
|
3
|
+
import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
4
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
|
+
/**
|
|
6
|
+
* TXE-specific subclass of PrivateExecutionOracle that forbids operations not supported in
|
|
7
|
+
* TestEnvironment::private_context. TXE uses dedicated oracle flows (e.g. private_call) instead.
|
|
8
|
+
*/
|
|
9
|
+
export declare class TXEPrivateExecutionOracle extends PrivateExecutionOracle {
|
|
10
|
+
callPrivateFunction(_targetContractAddress: AztecAddress, _functionSelector: FunctionSelector, _argsHash: Fr, _sideEffectCounter: number, _isStaticCall: boolean): Promise<{
|
|
11
|
+
endSideEffectCounter: Fr;
|
|
12
|
+
returnsHash: Fr;
|
|
13
|
+
}>;
|
|
14
|
+
assertValidPublicCalldata(_calldataHash: Fr): Promise<void>;
|
|
15
|
+
notifyRevertiblePhaseStart(_minRevertibleSideEffectCounter: number): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhlX3ByaXZhdGVfZXhlY3V0aW9uX29yYWNsZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL29yYWNsZS90eGVfcHJpdmF0ZV9leGVjdXRpb25fb3JhY2xlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3pELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzlELE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDMUQsT0FBTyxLQUFLLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFaEU7OztHQUdHO0FBQ0gscUJBQWEseUJBQTBCLFNBQVEsc0JBQXNCO0lBQzFELG1CQUFtQixDQUMxQixzQkFBc0IsRUFBRSxZQUFZLEVBQ3BDLGlCQUFpQixFQUFFLGdCQUFnQixFQUNuQyxTQUFTLEVBQUUsRUFBRSxFQUNiLGtCQUFrQixFQUFFLE1BQU0sRUFDMUIsYUFBYSxFQUFFLE9BQU8sR0FDckIsT0FBTyxDQUFDO1FBQUUsb0JBQW9CLEVBQUUsRUFBRSxDQUFDO1FBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQTtLQUFFLENBQUMsQ0FJeEQ7SUFFUSx5QkFBeUIsQ0FBQyxhQUFhLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFbkU7SUFFUSwwQkFBMEIsQ0FBQywrQkFBK0IsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUUxRjtDQUNGIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"txe_private_execution_oracle.d.ts","sourceRoot":"","sources":["../../src/oracle/txe_private_execution_oracle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,sBAAsB;IAC1D,mBAAmB,CAC1B,sBAAsB,EAAE,YAAY,EACpC,iBAAiB,EAAE,gBAAgB,EACnC,SAAS,EAAE,EAAE,EACb,kBAAkB,EAAE,MAAM,EAC1B,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC;QAAE,oBAAoB,EAAE,EAAE,CAAC;QAAC,WAAW,EAAE,EAAE,CAAA;KAAE,CAAC,CAIxD;IAEQ,yBAAyB,CAAC,aAAa,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnE;IAEQ,0BAA0B,CAAC,+BAA+B,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PrivateExecutionOracle } from '@aztec/pxe/simulator';
|
|
2
|
+
/**
|
|
3
|
+
* TXE-specific subclass of PrivateExecutionOracle that forbids operations not supported in
|
|
4
|
+
* TestEnvironment::private_context. TXE uses dedicated oracle flows (e.g. private_call) instead.
|
|
5
|
+
*/ export class TXEPrivateExecutionOracle extends PrivateExecutionOracle {
|
|
6
|
+
callPrivateFunction(_targetContractAddress, _functionSelector, _argsHash, _sideEffectCounter, _isStaticCall) {
|
|
7
|
+
throw new Error('Contract calls are forbidden inside a `TestEnvironment::private_context`, use `private_call` instead');
|
|
8
|
+
}
|
|
9
|
+
assertValidPublicCalldata(_calldataHash) {
|
|
10
|
+
throw new Error('Enqueueing public calls is not supported in TestEnvironment::private_context');
|
|
11
|
+
}
|
|
12
|
+
notifyRevertiblePhaseStart(_minRevertibleSideEffectCounter) {
|
|
13
|
+
throw new Error('Enqueueing public calls is not supported in TestEnvironment::private_context');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the TXE RPC server. With `TXE_WORKERS=1` oracle calls run on the main thread (no
|
|
4
|
+
* worker_threads, no IPC overhead). With any other value oracle calls are
|
|
5
|
+
* routed to a pool of worker threads sized to that value, sticky by `session_id`.
|
|
6
|
+
*
|
|
7
|
+
* Each incoming TCP socket is tagged with the `session_id` of the first oracle call it carries —
|
|
8
|
+
* nargo uses one HTTP client per test, so the socket-to-session mapping is 1:1. When the socket
|
|
9
|
+
* closes (end of test), the dispatcher disposes the session and frees its world state + LMDB.
|
|
10
|
+
*
|
|
11
|
+
* Lives in its own module so the worker bundle does not pull in the HTTP server stack.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createTXERpcServer(logger: Logger): Promise<import("@aztec/foundation/json-rpc/server").SafeJsonRpcServer>;
|
|
14
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnBjX3NlcnZlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3JwY19zZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFlcEQ7Ozs7Ozs7Ozs7R0FVRztBQUNILHdCQUFzQixrQkFBa0IsQ0FBQyxNQUFNLEVBQUUsTUFBTSwwRUE0Q3REIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc_server.d.ts","sourceRoot":"","sources":["../src/rpc_server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAepD;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,0EA4CtD"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
|
|
2
|
+
import { TXEDispatcherPool, buildSharedContractStore } from './dispatcher_pool.js';
|
|
3
|
+
import { TXEDispatcher, TXEDispatcherApiSchema } from './index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Symbol used to tag an incoming TCP socket with the `session_id` it has been associated with.
|
|
6
|
+
* Hidden under a Symbol so we don't risk colliding with anything koa or http core adds.
|
|
7
|
+
*/ const SESSION_SYMBOL = Symbol('txeSessionId');
|
|
8
|
+
/**
|
|
9
|
+
* Creates the TXE RPC server. With `TXE_WORKERS=1` oracle calls run on the main thread (no
|
|
10
|
+
* worker_threads, no IPC overhead). With any other value oracle calls are
|
|
11
|
+
* routed to a pool of worker threads sized to that value, sticky by `session_id`.
|
|
12
|
+
*
|
|
13
|
+
* Each incoming TCP socket is tagged with the `session_id` of the first oracle call it carries —
|
|
14
|
+
* nargo uses one HTTP client per test, so the socket-to-session mapping is 1:1. When the socket
|
|
15
|
+
* closes (end of test), the dispatcher disposes the session and frees its world state + LMDB.
|
|
16
|
+
*
|
|
17
|
+
* Lives in its own module so the worker bundle does not pull in the HTTP server stack.
|
|
18
|
+
*/ export async function createTXERpcServer(logger) {
|
|
19
|
+
const workerCount = Number(process.env.TXE_WORKERS);
|
|
20
|
+
let dispatcher;
|
|
21
|
+
if (workerCount === 1) {
|
|
22
|
+
const { dataDir, schnorrClassId } = await buildSharedContractStore();
|
|
23
|
+
dispatcher = new TXEDispatcher(logger, {
|
|
24
|
+
contractStoreSourceDir: dataDir,
|
|
25
|
+
schnorrClassId
|
|
26
|
+
});
|
|
27
|
+
} else {
|
|
28
|
+
dispatcher = new TXEDispatcherPool(logger, {
|
|
29
|
+
workers: Number.isFinite(workerCount) && workerCount > 1 ? workerCount : undefined
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
const server = createSafeJsonRpcServer(dispatcher, TXEDispatcherApiSchema, {
|
|
33
|
+
http200OnError: true,
|
|
34
|
+
middlewares: [
|
|
35
|
+
async (ctx, next)=>{
|
|
36
|
+
// The body parser runs further down the chain, so `ctx.request.body` is populated only
|
|
37
|
+
// after `next()` resolves.
|
|
38
|
+
await next();
|
|
39
|
+
const socket = ctx.req.socket;
|
|
40
|
+
if (socket[SESSION_SYMBOL] !== undefined) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const body = ctx.request.body;
|
|
44
|
+
const sessionId = body && typeof body === 'object' && 'params' in body ? extractSessionId(body.params) : undefined;
|
|
45
|
+
if (sessionId === undefined) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
socket[SESSION_SYMBOL] = sessionId;
|
|
49
|
+
logger.debug(`Tagged socket with session`, {
|
|
50
|
+
sessionId,
|
|
51
|
+
remoteAddress: socket.remoteAddress,
|
|
52
|
+
remotePort: socket.remotePort
|
|
53
|
+
});
|
|
54
|
+
socket.once('close', ()=>{
|
|
55
|
+
logger.debug(`Disposing session on socket close`, {
|
|
56
|
+
sessionId
|
|
57
|
+
});
|
|
58
|
+
void dispatcher.disposeSession(sessionId);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
return server;
|
|
64
|
+
}
|
|
65
|
+
// Extracts `session_id` from a JSON-RPC `params` array. Always a `number` because session_id
|
|
66
|
+
// comes off `JSON.parse`, which never produces BigInt; values above MAX_SAFE_INTEGER lose
|
|
67
|
+
// precision but still work as a Map key.
|
|
68
|
+
function extractSessionId(params) {
|
|
69
|
+
if (!Array.isArray(params) || params.length === 0) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const first = params[0];
|
|
73
|
+
if (first && typeof first === 'object' && 'session_id' in first) {
|
|
74
|
+
const sid = first.session_id;
|
|
75
|
+
return typeof sid === 'number' ? sid : undefined;
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|