@aztec-labs/txe 6.0.0-nightly.20260829
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-D4XJULH3.js +3 -0
- package/dest/AuthRegistry-D4XJULH3.js.map +7 -0
- package/dest/ContractClassRegistry-Q76MXQZD.js +3 -0
- package/dest/ContractClassRegistry-Q76MXQZD.js.map +7 -0
- package/dest/ContractInstanceRegistry-CTRARU77.js +3 -0
- package/dest/ContractInstanceRegistry-CTRARU77.js.map +7 -0
- package/dest/FeeJuice-YLTJXWYJ.js +3 -0
- package/dest/FeeJuice-YLTJXWYJ.js.map +7 -0
- package/dest/HandshakeRegistry-BRPEG7GL.js +3 -0
- package/dest/HandshakeRegistry-BRPEG7GL.js.map +7 -0
- package/dest/MultiCallEntrypoint-CJHMYQT5.js +3 -0
- package/dest/MultiCallEntrypoint-CJHMYQT5.js.map +7 -0
- package/dest/SchnorrAccount-OB4N4QIM.js +3 -0
- package/dest/SchnorrAccount-OB4N4QIM.js.map +7 -0
- package/dest/SchnorrInitializerlessAccount-TWGKRUSY.js +3 -0
- package/dest/SchnorrInitializerlessAccount-TWGKRUSY.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 +22 -0
- package/dest/bin/index.d.ts +3 -0
- package/dest/bin/index.d.ts.map +1 -0
- package/dest/bin/index.js +4 -0
- 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-32UHX7YY.js +3 -0
- package/dest/chunk-32UHX7YY.js.map +7 -0
- package/dest/chunk-F4QVFWNM.js +258 -0
- package/dest/chunk-F4QVFWNM.js.map +7 -0
- package/dest/chunk-LILEKW2X.js +304 -0
- package/dest/chunk-LILEKW2X.js.map +7 -0
- package/dest/constants.d.ts +7 -0
- package/dest/constants.d.ts.map +1 -0
- package/dest/constants.js +10 -0
- package/dest/dispatcher_pool.d.ts +67 -0
- package/dest/dispatcher_pool.d.ts.map +1 -0
- package/dest/dispatcher_pool.js +288 -0
- package/dest/index.d.ts +52 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +102 -0
- package/dest/metafile.json +38805 -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 +90 -0
- package/dest/oracle/interfaces.d.ts.map +1 -0
- package/dest/oracle/interfaces.js +3 -0
- package/dest/oracle/noir-structs/gas_settings_data.d.ts +14 -0
- package/dest/oracle/noir-structs/gas_settings_data.d.ts.map +1 -0
- package/dest/oracle/noir-structs/gas_settings_data.js +1 -0
- package/dest/oracle/noir-structs/tx_effects_data.d.ts +11 -0
- package/dest/oracle/noir-structs/tx_effects_data.d.ts.map +1 -0
- package/dest/oracle/noir-structs/tx_effects_data.js +1 -0
- package/dest/oracle/tagging_secret_strategy.d.ts +12 -0
- package/dest/oracle/tagging_secret_strategy.d.ts.map +1 -0
- package/dest/oracle/tagging_secret_strategy.js +12 -0
- package/dest/oracle/test-resolver/default_fixtures.d.ts +16 -0
- package/dest/oracle/test-resolver/default_fixtures.d.ts.map +1 -0
- package/dest/oracle/test-resolver/default_fixtures.js +207 -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 +32 -0
- package/dest/oracle/test-resolver/resolver.d.ts +48 -0
- package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
- package/dest/oracle/test-resolver/resolver.js +181 -0
- package/dest/oracle/txe_oracle_public_context.d.ts +58 -0
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_public_context.js +170 -0
- package/dest/oracle/txe_oracle_registry.d.ts +164 -0
- package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_registry.js +753 -0
- package/dest/oracle/txe_oracle_top_level_context.d.ts +128 -0
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_top_level_context.js +815 -0
- 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 +140 -0
- package/dest/rpc_translator.d.ts.map +1 -0
- package/dest/rpc_translator.js +976 -0
- package/dest/server.bundle.js +3 -0
- package/dest/server.bundle.js.map +7 -0
- package/dest/state_machine/archiver.d.ts +32 -0
- package/dest/state_machine/archiver.d.ts.map +1 -0
- package/dest/state_machine/archiver.js +95 -0
- package/dest/state_machine/dummy_p2p_client.d.ts +69 -0
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -0
- package/dest/state_machine/dummy_p2p_client.js +180 -0
- package/dest/state_machine/global_variable_builder.d.ts +15 -0
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -0
- package/dest/state_machine/global_variable_builder.js +25 -0
- package/dest/state_machine/index.d.ts +22 -0
- package/dest/state_machine/index.d.ts.map +1 -0
- package/dest/state_machine/index.js +110 -0
- package/dest/state_machine/mock_epoch_cache.d.ts +43 -0
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -0
- package/dest/state_machine/mock_epoch_cache.js +88 -0
- package/dest/state_machine/synchronizer.d.ts +41 -0
- package/dest/state_machine/synchronizer.d.ts.map +1 -0
- package/dest/state_machine/synchronizer.js +70 -0
- package/dest/txe_session.d.ts +179 -0
- package/dest/txe_session.d.ts.map +1 -0
- package/dest/txe_session.js +697 -0
- package/dest/utils/block_creation.d.ts +28 -0
- package/dest/utils/block_creation.d.ts.map +1 -0
- package/dest/utils/block_creation.js +59 -0
- package/dest/utils/encoding.d.ts +194 -0
- package/dest/utils/encoding.d.ts.map +1 -0
- package/dest/utils/encoding.js +136 -0
- package/dest/utils/expected_failure_error.d.ts +4 -0
- package/dest/utils/expected_failure_error.d.ts.map +1 -0
- package/dest/utils/expected_failure_error.js +5 -0
- package/dest/utils/tx_effect_creation.d.ts +5 -0
- package/dest/utils/tx_effect_creation.d.ts.map +1 -0
- package/dest/utils/tx_effect_creation.js +12 -0
- package/dest/utils/txe_account_store.d.ts +10 -0
- package/dest/utils/txe_account_store.d.ts.map +1 -0
- package/dest/utils/txe_account_store.js +17 -0
- package/dest/utils/txe_artifact_resolver.d.ts +36 -0
- package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
- package/dest/utils/txe_artifact_resolver.js +160 -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/utils/txe_public_contract_data_source.js +51 -0
- 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 +115 -0
- package/src/bin/check_txe_oracle_version.ts +31 -0
- package/src/bin/index.ts +49 -0
- package/src/bin/oracle_test_server.ts +51 -0
- package/src/constants.ts +13 -0
- package/src/dispatcher_pool.ts +319 -0
- package/src/index.ts +152 -0
- package/src/msgpackr_fr_extension.ts +22 -0
- package/src/oracle/interfaces.ts +125 -0
- package/src/oracle/noir-structs/gas_settings_data.ts +16 -0
- package/src/oracle/noir-structs/tx_effects_data.ts +11 -0
- package/src/oracle/tagging_secret_strategy.ts +25 -0
- package/src/oracle/test-resolver/default_fixtures.ts +278 -0
- package/src/oracle/test-resolver/index.ts +43 -0
- package/src/oracle/test-resolver/resolver.ts +245 -0
- package/src/oracle/txe_oracle_public_context.ts +220 -0
- package/src/oracle/txe_oracle_registry.ts +501 -0
- package/src/oracle/txe_oracle_top_level_context.ts +1086 -0
- package/src/oracle/txe_oracle_version.ts +17 -0
- package/src/oracle/txe_private_execution_oracle.ts +30 -0
- package/src/rpc_server.ts +86 -0
- package/src/rpc_translator.ts +1229 -0
- package/src/state_machine/archiver.ts +118 -0
- package/src/state_machine/dummy_p2p_client.ts +264 -0
- package/src/state_machine/global_variable_builder.ts +39 -0
- package/src/state_machine/index.ts +152 -0
- package/src/state_machine/mock_epoch_cache.ts +111 -0
- package/src/state_machine/synchronizer.ts +105 -0
- package/src/txe_session.ts +1107 -0
- package/src/utils/block_creation.ts +91 -0
- package/src/utils/encoding.ts +171 -0
- package/src/utils/expected_failure_error.ts +5 -0
- package/src/utils/tx_effect_creation.ts +28 -0
- package/src/utils/txe_account_store.ts +23 -0
- package/src/utils/txe_artifact_resolver.ts +217 -0
- package/src/utils/txe_public_contract_data_source.ts +60 -0
- package/src/worker.ts +98 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { BackendType, Barretenberg, BarretenbergSync } from '@aztec/bb.js';
|
|
2
|
+
|
|
3
|
+
import { getSchnorrAccountContractArtifact } from '@aztec-labs/accounts/schnorr/lazy';
|
|
4
|
+
import type { Logger } from '@aztec-labs/foundation/log';
|
|
5
|
+
import { openEphemeralStore } from '@aztec-labs/kv-store/lmdb-v2';
|
|
6
|
+
import { LazyProtocolContractsProvider } from '@aztec-labs/protocol-contracts/providers/lazy';
|
|
7
|
+
import { ContractStore } from '@aztec-labs/pxe/client/lazy';
|
|
8
|
+
import { getStandardAuthRegistry } from '@aztec-labs/standard-contracts/auth-registry/lazy';
|
|
9
|
+
import { getStandardHandshakeRegistry } from '@aztec-labs/standard-contracts/handshake-registry/lazy';
|
|
10
|
+
import { getContractClassFromArtifact } from '@aztec-labs/stdlib/contract';
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { cpus } from 'node:os';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { Worker } from 'node:worker_threads';
|
|
15
|
+
|
|
16
|
+
import { TXE_REQUIRED_PROTOCOL_CONTRACTS } from './index.js';
|
|
17
|
+
import type { TXEForeignCallInput } from './index.js';
|
|
18
|
+
import type { ForeignCallResult } from './utils/encoding.js';
|
|
19
|
+
|
|
20
|
+
void Barretenberg.initSingleton({ backend: BackendType.Wasm, skipSrsInit: true, threads: 1 });
|
|
21
|
+
void BarretenbergSync.initSingleton({ backend: BackendType.Wasm });
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Opens a fresh LMDB in a tmp dir and writes the protocol contracts in
|
|
25
|
+
* {@link TXE_REQUIRED_PROTOCOL_CONTRACTS} plus the standard AuthRegistry, HandshakeRegistry, and the SchnorrAccount artifact,
|
|
26
|
+
* returning the directory path and the SchnorrAccount class id (hex). The store handle is intentionally kept
|
|
27
|
+
* alive: closing it would trigger the ephemeral-store cleanup hook and remove the tmp
|
|
28
|
+
* directory, so any worker that has not yet cloned would find it missing.
|
|
29
|
+
*/
|
|
30
|
+
export async function buildSharedContractStore(): Promise<{ dataDir: string; schnorrClassId: string }> {
|
|
31
|
+
const kvStore = await openEphemeralStore('txe-shared-contracts', undefined, 2);
|
|
32
|
+
const dataDir = kvStore.dataDirectory;
|
|
33
|
+
const contractStore = new ContractStore(kvStore);
|
|
34
|
+
const provider = new LazyProtocolContractsProvider();
|
|
35
|
+
const [protocolContracts, standardContracts, schnorrArtifact] = await Promise.all([
|
|
36
|
+
Promise.all(TXE_REQUIRED_PROTOCOL_CONTRACTS.map(name => provider.getProtocolContractArtifact(name))),
|
|
37
|
+
Promise.all([getStandardAuthRegistry(), getStandardHandshakeRegistry()]),
|
|
38
|
+
getSchnorrAccountContractArtifact(),
|
|
39
|
+
]);
|
|
40
|
+
const schnorrClass = await getContractClassFromArtifact(schnorrArtifact);
|
|
41
|
+
await Promise.all([
|
|
42
|
+
...[...protocolContracts, ...standardContracts].flatMap(({ instance, artifact, contractClass }) => [
|
|
43
|
+
contractStore.addContractArtifact(artifact, contractClass),
|
|
44
|
+
contractStore.addContractInstance(instance),
|
|
45
|
+
]),
|
|
46
|
+
contractStore.addContractArtifact(schnorrArtifact, schnorrClass),
|
|
47
|
+
]);
|
|
48
|
+
return { dataDir, schnorrClassId: schnorrClass.id.toString() };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolves `worker.bundle.js` whether this code is running unbundled (next to dispatcher_pool.js
|
|
53
|
+
* inside `dest/`) or bundled into `dest/bin/index.js` (one directory deeper). `import.meta.url`
|
|
54
|
+
* refers to whichever module the calling code actually lives in; we try both relative locations
|
|
55
|
+
* and use whichever exists.
|
|
56
|
+
*/
|
|
57
|
+
// worker.bundle.js is esbuild's bundling of src/worker.ts (see esbuild.config.mjs).
|
|
58
|
+
// @dependency ./worker.ts
|
|
59
|
+
function resolveWorkerBundlePath(): URL {
|
|
60
|
+
const candidates = [new URL('./worker.bundle.js', import.meta.url), new URL('../worker.bundle.js', import.meta.url)];
|
|
61
|
+
return candidates.find(u => existsSync(fileURLToPath(u))) ?? candidates[0];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface SerializedError {
|
|
65
|
+
message: string;
|
|
66
|
+
name?: string;
|
|
67
|
+
stack?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type WorkerMessage =
|
|
71
|
+
| { type: 'result'; requestId: number; ok: true; value: ForeignCallResult }
|
|
72
|
+
| { type: 'result'; requestId: number; ok: false; error: SerializedError }
|
|
73
|
+
| {
|
|
74
|
+
type: 'memstat';
|
|
75
|
+
sessions: number;
|
|
76
|
+
rss: number;
|
|
77
|
+
heapTotal: number;
|
|
78
|
+
heapUsed: number;
|
|
79
|
+
external: number;
|
|
80
|
+
arrayBuffers: number;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
interface WorkerSlot {
|
|
84
|
+
worker: Worker;
|
|
85
|
+
sessions: Set<number>;
|
|
86
|
+
inFlightRequestIds: Set<number>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface PendingRequest {
|
|
90
|
+
resolve: (value: ForeignCallResult) => void;
|
|
91
|
+
reject: (err: Error) => void;
|
|
92
|
+
workerIdx: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function deserializeError(payload: SerializedError | undefined): Error {
|
|
96
|
+
if (!payload) {
|
|
97
|
+
return new Error('Worker returned an error with no payload');
|
|
98
|
+
}
|
|
99
|
+
const err = new Error(payload.message);
|
|
100
|
+
if (payload.name) {
|
|
101
|
+
err.name = payload.name;
|
|
102
|
+
}
|
|
103
|
+
if (payload.stack) {
|
|
104
|
+
err.stack = payload.stack;
|
|
105
|
+
}
|
|
106
|
+
return err;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface TXEDispatcherPoolOptions {
|
|
110
|
+
/** Number of worker threads */
|
|
111
|
+
workers?: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Main-thread router that owns a pool of TXE worker threads. Each worker runs its own
|
|
116
|
+
* {@link TXEDispatcher} and handles foreign-call requests for the sessions assigned to it.
|
|
117
|
+
*
|
|
118
|
+
* Routing is sticky by `session_id`: new sessions go to a freshly spawned worker (up to
|
|
119
|
+
* `maxWorkers`) or, once the cap is reached, to the existing worker with the fewest sessions.
|
|
120
|
+
* Each session's state — TXESession, native world state, KV stores — stays single-threaded
|
|
121
|
+
* within its worker; different sessions run in parallel across workers.
|
|
122
|
+
*
|
|
123
|
+
* The main thread builds a shared protocol-contracts LMDB once and passes its path via
|
|
124
|
+
* `workerData`. Workers clone the data file on demand instead of re-registering the contracts.
|
|
125
|
+
*/
|
|
126
|
+
export class TXEDispatcherPool {
|
|
127
|
+
private readonly workers: WorkerSlot[] = [];
|
|
128
|
+
private readonly sessionToWorker = new Map<number, number>();
|
|
129
|
+
private readonly pending = new Map<number, PendingRequest>();
|
|
130
|
+
private nextRequestId = 0;
|
|
131
|
+
private readonly maxWorkers: number;
|
|
132
|
+
private readonly readyPromise: Promise<void>;
|
|
133
|
+
private contractStoreSourceDir?: string;
|
|
134
|
+
private schnorrClassId?: string;
|
|
135
|
+
private workerPath?: URL;
|
|
136
|
+
|
|
137
|
+
constructor(
|
|
138
|
+
private readonly logger: Logger,
|
|
139
|
+
opts: TXEDispatcherPoolOptions = {},
|
|
140
|
+
) {
|
|
141
|
+
// TXE still doesn't scale well beyond 16 workers due to contention
|
|
142
|
+
this.maxWorkers = Math.max(1, opts.workers ?? Math.min(cpus().length, 16));
|
|
143
|
+
this.readyPromise = this.init();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Resolves once the shared protocol-contracts store is built. Workers spawn lazily on demand. */
|
|
147
|
+
public ready(): Promise<void> {
|
|
148
|
+
return this.readyPromise;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private async init(): Promise<void> {
|
|
152
|
+
const t0 = Date.now();
|
|
153
|
+
const { dataDir, schnorrClassId } = await buildSharedContractStore();
|
|
154
|
+
this.contractStoreSourceDir = dataDir;
|
|
155
|
+
this.schnorrClassId = schnorrClassId;
|
|
156
|
+
this.workerPath = resolveWorkerBundlePath();
|
|
157
|
+
this.logger.debug(`TXE dispatcher pool ready (lazy spawn, cap=${this.maxWorkers})`, {
|
|
158
|
+
contractStoreSourceDir: dataDir,
|
|
159
|
+
schnorrClassId,
|
|
160
|
+
ms: Date.now() - t0,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Spawns a fresh worker and returns its index in `this.workers`. Caller must have awaited
|
|
166
|
+
* `init()` so the shared contract store path is set. Messages posted before the worker has
|
|
167
|
+
* finished loading are queued by Node's worker_threads transport.
|
|
168
|
+
*/
|
|
169
|
+
private spawnWorker(): number {
|
|
170
|
+
const workerIdx = this.workers.length;
|
|
171
|
+
const w = new Worker(this.workerPath!, {
|
|
172
|
+
workerData: { contractStoreSourceDir: this.contractStoreSourceDir, schnorrClassId: this.schnorrClassId },
|
|
173
|
+
});
|
|
174
|
+
const slot: WorkerSlot = {
|
|
175
|
+
worker: w,
|
|
176
|
+
sessions: new Set(),
|
|
177
|
+
inFlightRequestIds: new Set(),
|
|
178
|
+
};
|
|
179
|
+
this.workers.push(slot);
|
|
180
|
+
w.on('message', (msg: WorkerMessage) => this.handleMessage(workerIdx, msg));
|
|
181
|
+
w.on('error', err => this.handleWorkerError(workerIdx, err));
|
|
182
|
+
w.on('exit', code => {
|
|
183
|
+
if (code !== 0) {
|
|
184
|
+
this.handleWorkerError(workerIdx, new Error(`Worker ${workerIdx} exited with code ${code}`));
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
this.logger.debug(`Spawning TXE worker ${workerIdx} on demand`, {
|
|
188
|
+
cap: this.maxWorkers,
|
|
189
|
+
poolSize: this.workers.length,
|
|
190
|
+
});
|
|
191
|
+
return workerIdx;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Routes a session-dispose request to the worker that owns the session. Fire-and-forget. */
|
|
195
|
+
disposeSession(sessionId: number): void {
|
|
196
|
+
const workerIdx = this.sessionToWorker.get(sessionId);
|
|
197
|
+
if (workerIdx === undefined) {
|
|
198
|
+
throw new Error(`disposeSession: no worker mapped for session ${sessionId}`);
|
|
199
|
+
}
|
|
200
|
+
this.sessionToWorker.delete(sessionId);
|
|
201
|
+
const slot = this.workers[workerIdx];
|
|
202
|
+
if (!slot) {
|
|
203
|
+
throw new Error(`disposeSession: worker ${workerIdx} (session ${sessionId}) missing from pool`);
|
|
204
|
+
}
|
|
205
|
+
slot.sessions.delete(sessionId);
|
|
206
|
+
slot.worker.postMessage({ type: 'dispose-session', sessionId });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// eslint-disable-next-line camelcase
|
|
210
|
+
async resolve_foreign_call(callData: TXEForeignCallInput): Promise<ForeignCallResult> {
|
|
211
|
+
// Make sure the shared contract store + worker bundle path are in place before we spawn.
|
|
212
|
+
await this.readyPromise;
|
|
213
|
+
const sessionId = callData.session_id;
|
|
214
|
+
let workerIdx = this.sessionToWorker.get(sessionId);
|
|
215
|
+
if (workerIdx === undefined) {
|
|
216
|
+
workerIdx = this.pickOrSpawnWorker();
|
|
217
|
+
this.sessionToWorker.set(sessionId, workerIdx);
|
|
218
|
+
this.workers[workerIdx].sessions.add(sessionId);
|
|
219
|
+
this.logger.debug(`Routing new session ${sessionId} to worker ${workerIdx}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const slot = this.workers[workerIdx];
|
|
223
|
+
const requestId = this.nextRequestId++;
|
|
224
|
+
return new Promise<ForeignCallResult>((resolve, reject) => {
|
|
225
|
+
this.pending.set(requestId, { resolve, reject, workerIdx: workerIdx! });
|
|
226
|
+
slot.inFlightRequestIds.add(requestId);
|
|
227
|
+
slot.worker.postMessage({ type: 'foreign-call', requestId, callData });
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Terminates all workers. Used by tests; production TXE exits the process directly. */
|
|
232
|
+
async shutdown(): Promise<void> {
|
|
233
|
+
const slots = this.workers.splice(0, this.workers.length);
|
|
234
|
+
await Promise.all(slots.map(s => s.worker.terminate()));
|
|
235
|
+
for (const [requestId, pending] of this.pending) {
|
|
236
|
+
pending.reject(new Error('TXE dispatcher pool was shut down'));
|
|
237
|
+
this.pending.delete(requestId);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Returns the index of the worker that should handle a new session. Spawns a fresh worker if
|
|
243
|
+
* the pool hasn't reached its cap; otherwise picks the existing worker with the fewest
|
|
244
|
+
* assigned sessions. Always returns a valid index into `this.workers`.
|
|
245
|
+
*/
|
|
246
|
+
private pickOrSpawnWorker(): number {
|
|
247
|
+
if (this.workers.length < this.maxWorkers) {
|
|
248
|
+
return this.spawnWorker();
|
|
249
|
+
}
|
|
250
|
+
let minIdx = 0;
|
|
251
|
+
let minCount = this.workers[0].sessions.size;
|
|
252
|
+
for (let i = 1; i < this.workers.length; i++) {
|
|
253
|
+
const count = this.workers[i].sessions.size;
|
|
254
|
+
if (count < minCount) {
|
|
255
|
+
minIdx = i;
|
|
256
|
+
minCount = count;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return minIdx;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private handleMessage(workerIdx: number, msg: WorkerMessage): void {
|
|
263
|
+
switch (msg.type) {
|
|
264
|
+
case 'result':
|
|
265
|
+
this.handleResult(workerIdx, msg);
|
|
266
|
+
return;
|
|
267
|
+
case 'memstat':
|
|
268
|
+
this.logger.debug(`worker ${workerIdx} memstat`, {
|
|
269
|
+
worker: workerIdx,
|
|
270
|
+
sessions: msg.sessions,
|
|
271
|
+
rssMiB: Math.round(msg.rss / 1024 / 1024),
|
|
272
|
+
heapTotalMiB: Math.round(msg.heapTotal / 1024 / 1024),
|
|
273
|
+
heapUsedMiB: Math.round(msg.heapUsed / 1024 / 1024),
|
|
274
|
+
externalMiB: Math.round(msg.external / 1024 / 1024),
|
|
275
|
+
arrayBuffersMiB: Math.round(msg.arrayBuffers / 1024 / 1024),
|
|
276
|
+
});
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private handleResult(workerIdx: number, msg: WorkerMessage & { type: 'result' }): void {
|
|
282
|
+
const pending = this.pending.get(msg.requestId);
|
|
283
|
+
if (!pending) {
|
|
284
|
+
throw new Error(`handleResult: request ${msg.requestId} (worker ${workerIdx}) not in pending map`);
|
|
285
|
+
}
|
|
286
|
+
this.pending.delete(msg.requestId);
|
|
287
|
+
const slot = this.workers[workerIdx];
|
|
288
|
+
if (!slot) {
|
|
289
|
+
throw new Error(`handleResult: worker ${workerIdx} (request ${msg.requestId}) missing from pool`);
|
|
290
|
+
}
|
|
291
|
+
slot.inFlightRequestIds.delete(msg.requestId);
|
|
292
|
+
if (msg.ok) {
|
|
293
|
+
pending.resolve(msg.value);
|
|
294
|
+
} else {
|
|
295
|
+
pending.reject(deserializeError(msg.error));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private handleWorkerError(workerIdx: number, err: Error): void {
|
|
300
|
+
this.logger.error(`TXE worker ${workerIdx} crashed; sessions assigned to it will fail`, err);
|
|
301
|
+
const slot = this.workers[workerIdx];
|
|
302
|
+
if (!slot) {
|
|
303
|
+
throw new Error(`handleWorkerError: worker ${workerIdx} missing from pool (orig err: ${err.message})`);
|
|
304
|
+
}
|
|
305
|
+
for (const requestId of slot.inFlightRequestIds) {
|
|
306
|
+
const pending = this.pending.get(requestId);
|
|
307
|
+
if (!pending) {
|
|
308
|
+
throw new Error(`handleWorkerError: in-flight request ${requestId} (worker ${workerIdx}) not in pending map`);
|
|
309
|
+
}
|
|
310
|
+
pending.reject(err);
|
|
311
|
+
this.pending.delete(requestId);
|
|
312
|
+
}
|
|
313
|
+
slot.inFlightRequestIds.clear();
|
|
314
|
+
for (const sessionId of slot.sessions) {
|
|
315
|
+
this.sessionToWorker.delete(sessionId);
|
|
316
|
+
}
|
|
317
|
+
slot.sessions.clear();
|
|
318
|
+
}
|
|
319
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Fr } from '@aztec-labs/foundation/curves/bn254';
|
|
2
|
+
import type { Logger } from '@aztec-labs/foundation/log';
|
|
3
|
+
import { cloneEphemeralStoreFrom } from '@aztec-labs/kv-store/lmdb-v2';
|
|
4
|
+
import type { ProtocolContractName } from '@aztec-labs/protocol-contracts';
|
|
5
|
+
import { ContractStore } from '@aztec-labs/pxe/client/lazy';
|
|
6
|
+
import type { ApiSchemaFor } from '@aztec-labs/stdlib/schemas';
|
|
7
|
+
import { zodFor } from '@aztec-labs/stdlib/schemas';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
// Side-effect import: registers the msgpackr Fr extension for the bundled `Fr` class. Must
|
|
12
|
+
// be loaded before any `sendMessage` call. See msgpackr_fr_extension.ts for the why.
|
|
13
|
+
import './msgpackr_fr_extension.js';
|
|
14
|
+
import { type TXEOracleFunctionName, TXESession } from './txe_session.js';
|
|
15
|
+
import {
|
|
16
|
+
type ForeignCallArgs,
|
|
17
|
+
ForeignCallArgsSchema,
|
|
18
|
+
type ForeignCallResult,
|
|
19
|
+
ForeignCallResultSchema,
|
|
20
|
+
} from './utils/encoding.js';
|
|
21
|
+
import { TXEArtifactResolver } from './utils/txe_artifact_resolver.js';
|
|
22
|
+
|
|
23
|
+
export const TXE_REQUIRED_PROTOCOL_CONTRACTS: ProtocolContractName[] = [
|
|
24
|
+
'ContractClassRegistry',
|
|
25
|
+
'ContractInstanceRegistry',
|
|
26
|
+
'FeeJuice',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const sessions = new Map<number, TXESession>();
|
|
30
|
+
|
|
31
|
+
export type TXEForeignCallInput = {
|
|
32
|
+
session_id: number;
|
|
33
|
+
function: TXEOracleFunctionName;
|
|
34
|
+
root_path: string;
|
|
35
|
+
package_name: string;
|
|
36
|
+
inputs: ForeignCallArgs;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const TXEForeignCallInputSchema: z.ZodType<TXEForeignCallInput> = zodFor<TXEForeignCallInput>()(
|
|
40
|
+
z.object({
|
|
41
|
+
// Nargo generates session_id as a u64, which may exceed Number.MAX_SAFE_INTEGER.
|
|
42
|
+
// Zod 4's `.int()` enforces the safe-integer bound, so we drop it here and only require
|
|
43
|
+
// the value to be a non-negative number (it is used solely as a Map key).
|
|
44
|
+
// eslint-disable-next-line camelcase
|
|
45
|
+
session_id: z.number().nonnegative(),
|
|
46
|
+
function: z.string() as z.ZodType<TXEOracleFunctionName>,
|
|
47
|
+
// eslint-disable-next-line camelcase
|
|
48
|
+
root_path: z.string(),
|
|
49
|
+
// eslint-disable-next-line camelcase
|
|
50
|
+
package_name: z.string(),
|
|
51
|
+
inputs: ForeignCallArgsSchema,
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export interface TXEDispatcherOptions {
|
|
56
|
+
/**
|
|
57
|
+
* Path to an LMDB directory holding the required protocol contracts (see
|
|
58
|
+
* {@link TXE_REQUIRED_PROTOCOL_CONTRACTS}) and the SchnorrAccount artifact. When set, the
|
|
59
|
+
* dispatcher clones this directory into a fresh tmpdir on first use instead of registering
|
|
60
|
+
* the contracts itself.
|
|
61
|
+
*/
|
|
62
|
+
contractStoreSourceDir: string;
|
|
63
|
+
/**
|
|
64
|
+
* Class id (hex) of the SchnorrAccount artifact pre-registered in the shared LMDB. The
|
|
65
|
+
* {@link TXEArtifactResolver} looks the artifact up from the cloned store via this class id
|
|
66
|
+
* instead of recomputing it via `getSchnorrAccountContractArtifact()` + `computeArtifactHash()`.
|
|
67
|
+
*/
|
|
68
|
+
schnorrClassId: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class TXEDispatcher {
|
|
72
|
+
private contractStore!: ContractStore;
|
|
73
|
+
private artifactResolver!: TXEArtifactResolver;
|
|
74
|
+
private readonly contractStoreSourceDir: string;
|
|
75
|
+
private readonly schnorrClassId: Fr;
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
private logger: Logger,
|
|
79
|
+
opts: TXEDispatcherOptions,
|
|
80
|
+
) {
|
|
81
|
+
this.contractStoreSourceDir = opts.contractStoreSourceDir;
|
|
82
|
+
this.schnorrClassId = Fr.fromString(opts.schnorrClassId);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Clones the pre-populated LMDB at `contractStoreSourceDir` into a fresh per-instance tmpdir
|
|
87
|
+
* on first use, so this dispatcher has a writable store already containing the required
|
|
88
|
+
* protocol contracts + SchnorrAccount. Idempotent — subsequent calls are no-ops.
|
|
89
|
+
*/
|
|
90
|
+
private async warmUp(): Promise<void> {
|
|
91
|
+
if (this.contractStore) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const t0 = Date.now();
|
|
95
|
+
const kvStore = await cloneEphemeralStoreFrom(
|
|
96
|
+
join(this.contractStoreSourceDir, 'data.mdb'),
|
|
97
|
+
'txe-contracts',
|
|
98
|
+
undefined,
|
|
99
|
+
2,
|
|
100
|
+
);
|
|
101
|
+
this.contractStore = new ContractStore(kvStore);
|
|
102
|
+
this.artifactResolver = new TXEArtifactResolver(this.contractStore, this.schnorrClassId);
|
|
103
|
+
this.logger.debug('Cloned shared protocol-contracts store', { totalMs: Date.now() - t0 });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// eslint-disable-next-line camelcase
|
|
107
|
+
async resolve_foreign_call(callData: TXEForeignCallInput): Promise<ForeignCallResult> {
|
|
108
|
+
const {
|
|
109
|
+
session_id: sessionId,
|
|
110
|
+
function: functionName,
|
|
111
|
+
inputs,
|
|
112
|
+
root_path: rootPath,
|
|
113
|
+
package_name: packageName,
|
|
114
|
+
} = callData;
|
|
115
|
+
this.logger.debug(`Calling ${functionName} on session ${sessionId}`);
|
|
116
|
+
|
|
117
|
+
if (!sessions.has(sessionId)) {
|
|
118
|
+
this.logger.debug(`Creating new session ${sessionId}`);
|
|
119
|
+
await this.warmUp();
|
|
120
|
+
sessions.set(sessionId, await TXESession.init(this.contractStore, this.artifactResolver, rootPath, packageName));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return await sessions.get(sessionId)!.processFunction(functionName, inputs);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Releases a session and its resources (per-session LMDB + `NativeWorldStateService`).
|
|
128
|
+
* Called by the dispatcher pool when nargo closes its TCP connection for a test (see
|
|
129
|
+
* `rpc_server.ts`'s socket tracker). No-op if the session was never created — that happens
|
|
130
|
+
* when nargo opens a connection but errors before sending a request.
|
|
131
|
+
*/
|
|
132
|
+
async disposeSession(sessionId: number): Promise<void> {
|
|
133
|
+
const session = sessions.get(sessionId);
|
|
134
|
+
if (!session) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
sessions.delete(sessionId);
|
|
138
|
+
await session.dispose();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Diagnostic-only: number of sessions currently held by this worker. */
|
|
143
|
+
export function activeSessionCount(): number {
|
|
144
|
+
return sessions.size;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const TXEDispatcherApiSchema: ApiSchemaFor<TXEDispatcher> = {
|
|
148
|
+
// eslint-disable-next-line camelcase
|
|
149
|
+
resolve_foreign_call: z.function({ input: z.tuple([TXEForeignCallInputSchema]), output: ForeignCallResultSchema }),
|
|
150
|
+
// disposeSession is invoked over IPC from the worker, not via RPC; required by ApiSchemaFor.
|
|
151
|
+
disposeSession: z.function({ input: z.tuple([z.number().nonnegative()]), output: z.void() }),
|
|
152
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Fr } from '@aztec-labs/foundation/curves/bn254';
|
|
2
|
+
import { addExtension } from 'msgpackr';
|
|
3
|
+
|
|
4
|
+
// `@aztec-labs/native/msgpack_channel` registers a msgpackr extension that packs `Fr` instances to
|
|
5
|
+
// their raw 32-byte buffer for the C++ world-state side to deserialize. Because `@aztec-labs/native`
|
|
6
|
+
// is externalized in TXE's esbuild config (it loads a `.node` binary that can't be bundled),
|
|
7
|
+
// the `Fr` class the extension is keyed on belongs to the *external* `@aztec-labs/foundation`
|
|
8
|
+
// module instance — a different class identity than the `Fr` bundled into the TXE worker /
|
|
9
|
+
// bin. msgpackr's extension match uses `instanceof`, so bundled `Fr` instances slip through
|
|
10
|
+
// and msgpackr falls back to `Fr.toJSON()` (returns `Fr.toString()` — a `0x...` hex string).
|
|
11
|
+
// The C++ side then sees a string where a 32-byte binary was expected and throws
|
|
12
|
+
// `msgpack::type_error` whose `what()` is literally `"std::bad_cast"`.
|
|
13
|
+
//
|
|
14
|
+
// Fix: register the same extension under the BUNDLED `Fr` class identity. msgpackr's extension
|
|
15
|
+
// table holds an entry per `Class`, so both Fr classes now resolve to the same `write` callback
|
|
16
|
+
// and produce the same wire format. Imported as a side-effect from `./index.js` so both the
|
|
17
|
+
// worker (via `./worker.ts`) and the main-thread RPC server (via `./rpc_server.ts`) get the
|
|
18
|
+
// registration before any `sendMessage` runs.
|
|
19
|
+
addExtension({
|
|
20
|
+
Class: Fr,
|
|
21
|
+
write: (fr: Fr) => fr.toBuffer(),
|
|
22
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { BlockNumber } from '@aztec-labs/foundation/branded-types';
|
|
2
|
+
import type { Fr } from '@aztec-labs/foundation/curves/bn254';
|
|
3
|
+
import type { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
4
|
+
import type { ChangeSetId, TaggingSecretStrategy } from '@aztec-labs/pxe/server';
|
|
5
|
+
import type { Option } from '@aztec-labs/pxe/simulator';
|
|
6
|
+
import type { EventSelector, FunctionSelector } from '@aztec-labs/stdlib/abi';
|
|
7
|
+
import type { AztecAddress } from '@aztec-labs/stdlib/aztec-address';
|
|
8
|
+
import { CompleteAddress } from '@aztec-labs/stdlib/contract';
|
|
9
|
+
import type { GasSettings } from '@aztec-labs/stdlib/gas';
|
|
10
|
+
import type { UInt64 } from '@aztec-labs/stdlib/types';
|
|
11
|
+
|
|
12
|
+
import type { TxEffectsData } from './noir-structs/tx_effects_data.js';
|
|
13
|
+
|
|
14
|
+
// These interfaces complement the ones defined in PXE, and combined with those contain the full list of oracles used by
|
|
15
|
+
// aztec-nr. In particular, these include the ones needed to run Brillig code associated to #[external("public")] functions that has
|
|
16
|
+
// not been transpiled (e.g. in the context of a Noir test) as well as the ones associated with managing the state of
|
|
17
|
+
// such a Noir test (deploying contracts, manipulating block time, making calls, etc) - the so called 'top level test
|
|
18
|
+
// context'.
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Oracle methods associated with the execution of an Aztec #[external("public")] function.
|
|
22
|
+
*
|
|
23
|
+
* Note that real contracts have their Brillig calls to these be transpiled into opcodes, the oracles are only executed
|
|
24
|
+
* as such when running the original Brillig code, e.g. when invoking functions that interact with a PublicContext
|
|
25
|
+
* directly in a Noir test.
|
|
26
|
+
*/
|
|
27
|
+
export interface IAvmExecutionOracle {
|
|
28
|
+
isAvm: true;
|
|
29
|
+
|
|
30
|
+
address(): Promise<AztecAddress>;
|
|
31
|
+
sender(): Promise<AztecAddress>;
|
|
32
|
+
blockNumber(): Promise<BlockNumber>;
|
|
33
|
+
timestamp(): Promise<bigint>;
|
|
34
|
+
isStaticCall(): Promise<boolean>;
|
|
35
|
+
chainId(): Promise<Fr>;
|
|
36
|
+
version(): Promise<Fr>;
|
|
37
|
+
emitNullifier(nullifier: Fr): Promise<void>;
|
|
38
|
+
emitNoteHash(noteHash: Fr): Promise<void>;
|
|
39
|
+
nullifierExists(siloedNullifier: Fr): Promise<boolean>;
|
|
40
|
+
storageWrite(slot: Fr, value: Fr): Promise<void>;
|
|
41
|
+
storageRead(slot: Fr, contractAddress: AztecAddress): Promise<Fr>;
|
|
42
|
+
getContractInstanceDeployer(address: AztecAddress): Promise<{ member: Fr; exists: boolean }[]>;
|
|
43
|
+
getContractInstanceClassId(address: AztecAddress): Promise<{ member: Fr; exists: boolean }[]>;
|
|
44
|
+
getContractInstanceInitializationHash(address: AztecAddress): Promise<{ member: Fr; exists: boolean }[]>;
|
|
45
|
+
getContractInstanceImmutablesHash(address: AztecAddress): Promise<{ member: Fr; exists: boolean }[]>;
|
|
46
|
+
returndataSize(): Promise<number>;
|
|
47
|
+
returndataCopy(rdOffset: number, copySize: number): Promise<Fr[]>;
|
|
48
|
+
call(l2Gas: number, daGas: number, address: AztecAddress, argsLength: number, args: Fr[]): Promise<void>;
|
|
49
|
+
staticCall(l2Gas: number, daGas: number, address: AztecAddress, argsLength: number, args: Fr[]): Promise<void>;
|
|
50
|
+
successCopy(): Promise<boolean>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Oracle methods associated with the execution of an Aztec Noir test.
|
|
55
|
+
*/
|
|
56
|
+
export interface ITxeExecutionOracle {
|
|
57
|
+
isTxe: true;
|
|
58
|
+
|
|
59
|
+
getDefaultAddress(): AztecAddress;
|
|
60
|
+
getNextBlockNumber(): Promise<BlockNumber>;
|
|
61
|
+
getNextBlockTimestamp(): Promise<UInt64>;
|
|
62
|
+
advanceBlocksBy(blocks: number): Promise<void>;
|
|
63
|
+
advanceTimestampBy(duration: UInt64): void;
|
|
64
|
+
deploy(
|
|
65
|
+
contractPath: string,
|
|
66
|
+
initializer: string,
|
|
67
|
+
args: Fr[],
|
|
68
|
+
secret: Fr,
|
|
69
|
+
salt: Fr,
|
|
70
|
+
deployer: AztecAddress,
|
|
71
|
+
): Promise<Fr[]>;
|
|
72
|
+
createAccount(secret: Fr, partialAddress: Fr): Promise<CompleteAddress>;
|
|
73
|
+
addAccount(secret: Fr): Promise<CompleteAddress>;
|
|
74
|
+
addAuthWitness(address: AztecAddress, messageHash: Fr): Promise<void>;
|
|
75
|
+
sendL1ToL2Message(content: Fr, secretHash: Fr, sender: EthAddress, recipient: AztecAddress): Promise<Fr>;
|
|
76
|
+
/**
|
|
77
|
+
* Configures the tagging secret strategy the test's simulated wallet resolves for each delivery mode. A `none`
|
|
78
|
+
* clears that mode, so it falls back to the default strategy (or, when both modes end up unset, to no hook at all).
|
|
79
|
+
*/
|
|
80
|
+
setTaggingSecretStrategies(
|
|
81
|
+
unconstrainedStrategy: Option<TaggingSecretStrategy>,
|
|
82
|
+
constrainedStrategy: Option<TaggingSecretStrategy>,
|
|
83
|
+
): void;
|
|
84
|
+
/**
|
|
85
|
+
* Configures whether the test's simulated wallet authorizes every cross-contract utility call target.
|
|
86
|
+
*/
|
|
87
|
+
setAuthorizeAllUtilityCallTargets(authorizeAll: boolean): void;
|
|
88
|
+
getLastBlockTimestamp(): Promise<bigint>;
|
|
89
|
+
getLastTxEffects(): Promise<TxEffectsData>;
|
|
90
|
+
getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress): Promise<Fr[][]>;
|
|
91
|
+
privateCallNewFlow(
|
|
92
|
+
from: AztecAddress | undefined,
|
|
93
|
+
targetContractAddress: AztecAddress,
|
|
94
|
+
functionSelector: FunctionSelector,
|
|
95
|
+
args: Fr[],
|
|
96
|
+
argsHash: Fr,
|
|
97
|
+
isStaticCall: boolean,
|
|
98
|
+
additionalScopes: AztecAddress[],
|
|
99
|
+
changeSetId: ChangeSetId,
|
|
100
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
101
|
+
gasSettings: GasSettings,
|
|
102
|
+
): Promise<{ returnValues: Fr[]; offchainEffects: Fr[][] }>;
|
|
103
|
+
executeUtilityFunction(
|
|
104
|
+
from: AztecAddress | undefined,
|
|
105
|
+
targetContractAddress: AztecAddress,
|
|
106
|
+
functionSelector: FunctionSelector,
|
|
107
|
+
args: Fr[],
|
|
108
|
+
changeSetId: ChangeSetId,
|
|
109
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
110
|
+
): Promise<Fr[]>;
|
|
111
|
+
publicCallNewFlow(
|
|
112
|
+
from: AztecAddress | undefined,
|
|
113
|
+
targetContractAddress: AztecAddress,
|
|
114
|
+
calldata: Fr[],
|
|
115
|
+
isStaticCall: boolean,
|
|
116
|
+
gasSettings: GasSettings,
|
|
117
|
+
): Promise<Fr[]>;
|
|
118
|
+
// TODO(F-335): Drop this from here as it's not a real oracle handler - it's only called from
|
|
119
|
+
// RPCTranslator::txeGetPrivateEvents and never from Noir.
|
|
120
|
+
syncContractNonOracleMethod(
|
|
121
|
+
contractAddress: AztecAddress,
|
|
122
|
+
scope: AztecAddress,
|
|
123
|
+
changeSetId: ChangeSetId,
|
|
124
|
+
): Promise<void>;
|
|
125
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FieldsOf } from '@aztec-labs/foundation/types';
|
|
2
|
+
import type { Gas, GasFees } from '@aztec-labs/stdlib/gas';
|
|
3
|
+
|
|
4
|
+
/** Wire form of {@link Gas}: the domain type's fields as decoded off the wire, before conversion. */
|
|
5
|
+
export type GasData = FieldsOf<Gas>;
|
|
6
|
+
|
|
7
|
+
/** Wire form of {@link GasFees}: the domain type's fields as decoded off the wire, before conversion. */
|
|
8
|
+
export type GasFeesData = FieldsOf<GasFees>;
|
|
9
|
+
|
|
10
|
+
/** Wire form of the gas usage and fee limits set by a transaction sender. */
|
|
11
|
+
export type GasSettingsData = {
|
|
12
|
+
gasLimits: GasData;
|
|
13
|
+
teardownGasLimits: GasData;
|
|
14
|
+
maxFeesPerGas: GasFeesData;
|
|
15
|
+
maxPriorityFeesPerGas: GasFeesData;
|
|
16
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Fr } from '@aztec-labs/foundation/curves/bn254';
|
|
2
|
+
import type { TxHash } from '@aztec-labs/stdlib/tx';
|
|
3
|
+
|
|
4
|
+
type TxPrivateLog = Fr[];
|
|
5
|
+
|
|
6
|
+
export type TxEffectsData = {
|
|
7
|
+
txHash: TxHash;
|
|
8
|
+
noteHashes: Fr[];
|
|
9
|
+
nullifiers: Fr[];
|
|
10
|
+
privateLogs: TxPrivateLog[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_TAGGING_SECRET_STRATEGY,
|
|
3
|
+
type ResolveTaggingSecretStrategy,
|
|
4
|
+
type TaggingSecretStrategy,
|
|
5
|
+
} from '@aztec-labs/pxe/server';
|
|
6
|
+
import type { AppTaggingSecretKind } from '@aztec-labs/stdlib/logs';
|
|
7
|
+
|
|
8
|
+
/** The tagging secret strategies a TXE test has configured, keyed by delivery mode. Absence means "not configured". */
|
|
9
|
+
export type TXETaggingSecretStrategies = Map<AppTaggingSecretKind, TaggingSecretStrategy>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Builds the `resolveTaggingSecretStrategy` hook backing the `aztec_txe_setTaggingSecretStrategies` oracle. Returns
|
|
13
|
+
* `undefined` when no strategy is configured, so PXE's own no-hook default path is exercised. When at least one mode
|
|
14
|
+
* is configured, modes without an entry resolve to {@link DEFAULT_TAGGING_SECRET_STRATEGY}, matching what PXE would
|
|
15
|
+
* apply without a hook.
|
|
16
|
+
*/
|
|
17
|
+
export function makeResolveTaggingSecretStrategyHook(
|
|
18
|
+
strategies: TXETaggingSecretStrategies,
|
|
19
|
+
): ResolveTaggingSecretStrategy | undefined {
|
|
20
|
+
if (strategies.size === 0) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return ({ deliveryMode }) => Promise.resolve(strategies.get(deliveryMode) ?? DEFAULT_TAGGING_SECRET_STRATEGY);
|
|
25
|
+
}
|