@aztec/txe 0.0.1-commit.f504929 → 0.0.1-commit.f5a9928

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.
Files changed (170) hide show
  1. package/dest/AuthRegistry-56AMRLSQ.js +3 -0
  2. package/dest/AuthRegistry-56AMRLSQ.js.map +7 -0
  3. package/dest/ContractClassRegistry-CRN27URB.js +3 -0
  4. package/dest/ContractClassRegistry-CRN27URB.js.map +7 -0
  5. package/dest/ContractInstanceRegistry-GRGL26IC.js +3 -0
  6. package/dest/ContractInstanceRegistry-GRGL26IC.js.map +7 -0
  7. package/dest/FeeJuice-J23RVHLW.js +3 -0
  8. package/dest/FeeJuice-J23RVHLW.js.map +7 -0
  9. package/dest/HandshakeRegistry-5.0.1-PEFCLEGP.js +3 -0
  10. package/dest/HandshakeRegistry-5.0.1-PEFCLEGP.js.map +7 -0
  11. package/dest/HandshakeRegistry-GNFACTEE.js +3 -0
  12. package/dest/HandshakeRegistry-GNFACTEE.js.map +7 -0
  13. package/dest/MultiCallEntrypoint-MIDICLQB.js +3 -0
  14. package/dest/MultiCallEntrypoint-MIDICLQB.js.map +7 -0
  15. package/dest/SchnorrAccount-ZRENHSMV.js +3 -0
  16. package/dest/SchnorrAccount-ZRENHSMV.js.map +7 -0
  17. package/dest/SchnorrInitializerlessAccount-L56LOFSV.js +3 -0
  18. package/dest/SchnorrInitializerlessAccount-L56LOFSV.js.map +7 -0
  19. package/dest/bin/check_txe_oracle_version.d.ts +2 -0
  20. package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
  21. package/dest/bin/check_txe_oracle_version.js +53 -0
  22. package/dest/bin/index.js +3 -30
  23. package/dest/bin/index.js.map +7 -0
  24. package/dest/bin/oracle_test_server.d.ts +3 -0
  25. package/dest/bin/oracle_test_server.d.ts.map +1 -0
  26. package/dest/bin/oracle_test_server.js +41 -0
  27. package/dest/chunk-BJVAAXNA.js +3 -0
  28. package/dest/chunk-BJVAAXNA.js.map +7 -0
  29. package/dest/chunk-C2WBKKGS.js +266 -0
  30. package/dest/chunk-C2WBKKGS.js.map +7 -0
  31. package/dest/chunk-OFS2CI6U.js +304 -0
  32. package/dest/chunk-OFS2CI6U.js.map +7 -0
  33. package/dest/constants.d.ts +5 -1
  34. package/dest/constants.d.ts.map +1 -1
  35. package/dest/constants.js +9 -1
  36. package/dest/dispatcher_pool.d.ts +67 -0
  37. package/dest/dispatcher_pool.d.ts.map +1 -0
  38. package/dest/dispatcher_pool.js +286 -0
  39. package/dest/index.d.ts +51 -7
  40. package/dest/index.d.ts.map +1 -1
  41. package/dest/index.js +72 -190
  42. package/dest/metafile.json +40416 -0
  43. package/dest/msgpackr_fr_extension.d.ts +2 -0
  44. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  45. package/dest/msgpackr_fr_extension.js +21 -0
  46. package/dest/oracle/interfaces.d.ts +47 -9
  47. package/dest/oracle/interfaces.d.ts.map +1 -1
  48. package/dest/oracle/tagging_secret_strategy.d.ts +12 -0
  49. package/dest/oracle/tagging_secret_strategy.d.ts.map +1 -0
  50. package/dest/oracle/tagging_secret_strategy.js +12 -0
  51. package/dest/oracle/test-resolver/default_fixtures.d.ts +16 -0
  52. package/dest/oracle/test-resolver/default_fixtures.d.ts.map +1 -0
  53. package/dest/oracle/test-resolver/default_fixtures.js +200 -0
  54. package/dest/oracle/test-resolver/index.d.ts +9 -0
  55. package/dest/oracle/test-resolver/index.d.ts.map +1 -0
  56. package/dest/oracle/test-resolver/index.js +32 -0
  57. package/dest/oracle/test-resolver/resolver.d.ts +48 -0
  58. package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
  59. package/dest/oracle/test-resolver/resolver.js +181 -0
  60. package/dest/oracle/txe_oracle_public_context.d.ts +26 -2
  61. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  62. package/dest/oracle/txe_oracle_public_context.js +49 -1
  63. package/dest/oracle/txe_oracle_registry.d.ts +172 -0
  64. package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
  65. package/dest/oracle/txe_oracle_registry.js +767 -0
  66. package/dest/oracle/txe_oracle_top_level_context.d.ts +58 -17
  67. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  68. package/dest/oracle/txe_oracle_top_level_context.js +259 -88
  69. package/dest/oracle/txe_oracle_version.d.ts +17 -0
  70. package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
  71. package/dest/oracle/txe_oracle_version.js +14 -0
  72. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  73. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  74. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  75. package/dest/rpc_server.d.ts +14 -0
  76. package/dest/rpc_server.d.ts.map +1 -0
  77. package/dest/rpc_server.js +78 -0
  78. package/dest/rpc_translator.d.ts +115 -228
  79. package/dest/rpc_translator.d.ts.map +1 -1
  80. package/dest/rpc_translator.js +791 -607
  81. package/dest/server.bundle.js +3 -0
  82. package/dest/server.bundle.js.map +7 -0
  83. package/dest/state_machine/archiver.d.ts +6 -5
  84. package/dest/state_machine/archiver.d.ts.map +1 -1
  85. package/dest/state_machine/archiver.js +28 -20
  86. package/dest/state_machine/dummy_p2p_client.d.ts +18 -8
  87. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  88. package/dest/state_machine/dummy_p2p_client.js +31 -4
  89. package/dest/state_machine/global_variable_builder.d.ts +10 -6
  90. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  91. package/dest/state_machine/global_variable_builder.js +8 -5
  92. package/dest/state_machine/index.d.ts +11 -5
  93. package/dest/state_machine/index.d.ts.map +1 -1
  94. package/dest/state_machine/index.js +50 -10
  95. package/dest/state_machine/mock_epoch_cache.d.ts +16 -3
  96. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  97. package/dest/state_machine/mock_epoch_cache.js +29 -2
  98. package/dest/state_machine/synchronizer.d.ts +9 -6
  99. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  100. package/dest/state_machine/synchronizer.js +10 -6
  101. package/dest/txe_session.d.ts +90 -19
  102. package/dest/txe_session.d.ts.map +1 -1
  103. package/dest/txe_session.js +315 -61
  104. package/dest/utils/block_creation.d.ts +2 -2
  105. package/dest/utils/block_creation.d.ts.map +1 -1
  106. package/dest/utils/block_creation.js +2 -2
  107. package/dest/utils/encoding.d.ts +192 -0
  108. package/dest/utils/encoding.d.ts.map +1 -0
  109. package/dest/{util → utils}/encoding.js +7 -2
  110. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  111. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  112. package/dest/utils/tx_effect_creation.d.ts +1 -1
  113. package/dest/utils/tx_effect_creation.d.ts.map +1 -1
  114. package/dest/utils/tx_effect_creation.js +0 -1
  115. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  116. package/dest/utils/txe_account_store.d.ts.map +1 -0
  117. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  118. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  119. package/dest/utils/txe_artifact_resolver.js +161 -0
  120. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  121. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  122. package/dest/{util → utils}/txe_public_contract_data_source.js +8 -7
  123. package/dest/worker.bundle.js +3 -0
  124. package/dest/worker.bundle.js.map +7 -0
  125. package/dest/worker.d.ts +2 -0
  126. package/dest/worker.d.ts.map +1 -0
  127. package/dest/worker.js +92 -0
  128. package/package.json +38 -21
  129. package/src/bin/check_txe_oracle_version.ts +67 -0
  130. package/src/bin/index.ts +11 -2
  131. package/src/bin/oracle_test_server.ts +51 -0
  132. package/src/constants.ts +11 -1
  133. package/src/dispatcher_pool.ts +317 -0
  134. package/src/index.ts +99 -227
  135. package/src/msgpackr_fr_extension.ts +23 -0
  136. package/src/oracle/interfaces.ts +47 -8
  137. package/src/oracle/tagging_secret_strategy.ts +25 -0
  138. package/src/oracle/test-resolver/default_fixtures.ts +271 -0
  139. package/src/oracle/test-resolver/index.ts +44 -0
  140. package/src/oracle/test-resolver/resolver.ts +245 -0
  141. package/src/oracle/txe_oracle_public_context.ts +62 -0
  142. package/src/oracle/txe_oracle_registry.ts +556 -0
  143. package/src/oracle/txe_oracle_top_level_context.ts +342 -97
  144. package/src/oracle/txe_oracle_version.ts +17 -0
  145. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  146. package/src/rpc_server.ts +87 -0
  147. package/src/rpc_translator.ts +882 -870
  148. package/src/state_machine/archiver.ts +41 -20
  149. package/src/state_machine/dummy_p2p_client.ts +49 -12
  150. package/src/state_machine/global_variable_builder.ts +12 -11
  151. package/src/state_machine/index.ts +63 -28
  152. package/src/state_machine/mock_epoch_cache.ts +38 -3
  153. package/src/state_machine/synchronizer.ts +13 -7
  154. package/src/txe_session.ts +552 -75
  155. package/src/utils/block_creation.ts +2 -1
  156. package/src/{util → utils}/encoding.ts +8 -2
  157. package/src/utils/tx_effect_creation.ts +0 -2
  158. package/src/utils/txe_artifact_resolver.ts +217 -0
  159. package/src/{util → utils}/txe_public_contract_data_source.ts +6 -6
  160. package/src/worker.ts +98 -0
  161. package/dest/util/encoding.d.ts +0 -720
  162. package/dest/util/encoding.d.ts.map +0 -1
  163. package/dest/util/expected_failure_error.d.ts.map +0 -1
  164. package/dest/util/txe_account_store.d.ts.map +0 -1
  165. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  166. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  167. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  168. /package/dest/{util → utils}/txe_account_store.js +0 -0
  169. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  170. /package/src/{util → utils}/txe_account_store.ts +0 -0
package/src/index.ts CHANGED
@@ -1,62 +1,35 @@
1
- import { SchnorrAccountContractArtifact } from '@aztec/accounts/schnorr';
2
- import { type NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js/abi';
3
- import { AztecAddress } from '@aztec/aztec.js/addresses';
4
- import {
5
- type ContractInstanceWithAddress,
6
- getContractInstanceFromInstantiationParams,
7
- } from '@aztec/aztec.js/contracts';
8
1
  import { Fr } from '@aztec/aztec.js/fields';
9
- import { PublicKeys, deriveKeys } from '@aztec/aztec.js/keys';
10
- import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
11
2
  import type { Logger } from '@aztec/foundation/log';
12
- import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
13
- import { protocolContractNames } from '@aztec/protocol-contracts';
14
- import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
15
- import { ContractStore } from '@aztec/pxe/server';
16
- import { computeArtifactHash } from '@aztec/stdlib/contract';
17
- import type { ContractArtifactWithHash } from '@aztec/stdlib/contract';
3
+ import { cloneEphemeralStoreFrom } from '@aztec/kv-store/lmdb-v2';
4
+ import type { ProtocolContractName } from '@aztec/protocol-contracts';
5
+ import { ContractStore } from '@aztec/pxe/client/lazy';
18
6
  import type { ApiSchemaFor } from '@aztec/stdlib/schemas';
19
7
  import { zodFor } from '@aztec/stdlib/schemas';
20
8
 
21
- import { createHash } from 'crypto';
22
- import { createReadStream } from 'fs';
23
- import { readFile, readdir } from 'fs/promises';
24
- import { join, parse } from 'path';
9
+ import { join } from 'path';
25
10
  import { z } from 'zod';
26
11
 
12
+ // Side-effect import: registers the msgpackr Fr extension for the bundled `Fr` class. Must
13
+ // be loaded before any `sendMessage` call. See msgpackr_fr_extension.ts for the why.
14
+ import './msgpackr_fr_extension.js';
27
15
  import { type TXEOracleFunctionName, TXESession } from './txe_session.js';
28
16
  import {
29
17
  type ForeignCallArgs,
30
18
  ForeignCallArgsSchema,
31
- type ForeignCallArray,
32
19
  type ForeignCallResult,
33
20
  ForeignCallResultSchema,
34
- type ForeignCallSingle,
35
- addressFromSingle,
36
- fromArray,
37
- fromSingle,
38
- toSingle,
39
- } from './util/encoding.js';
21
+ } from './utils/encoding.js';
22
+ import { TXEArtifactResolver } from './utils/txe_artifact_resolver.js';
40
23
 
41
- const sessions = new Map<number, TXESession>();
24
+ export const TXE_REQUIRED_PROTOCOL_CONTRACTS: ProtocolContractName[] = [
25
+ 'ContractClassRegistry',
26
+ 'ContractInstanceRegistry',
27
+ 'FeeJuice',
28
+ ];
42
29
 
43
- /*
44
- * TXE typically has to load the same contract artifacts over and over again for multiple tests,
45
- * so we cache them here to avoid loading from disk repeatedly.
46
- *
47
- * The in-flight map coalesces concurrent requests for the same cache key so that
48
- * computeArtifactHash (very expensive) is only run once even under parallelism.
49
- */
50
- const TXEArtifactsCache = new Map<
51
- string,
52
- { artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }
53
- >();
54
- const TXEArtifactsCacheInFlight = new Map<
55
- string,
56
- Promise<{ artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }>
57
- >();
30
+ const sessions = new Map<number, TXESession>();
58
31
 
59
- type TXEForeignCallInput = {
32
+ export type TXEForeignCallInput = {
60
33
  session_id: number;
61
34
  function: TXEOracleFunctionName;
62
35
  root_path: string;
@@ -64,10 +37,13 @@ type TXEForeignCallInput = {
64
37
  inputs: ForeignCallArgs;
65
38
  };
66
39
 
67
- const TXEForeignCallInputSchema = zodFor<TXEForeignCallInput>()(
40
+ export const TXEForeignCallInputSchema: z.ZodType<TXEForeignCallInput> = zodFor<TXEForeignCallInput>()(
68
41
  z.object({
42
+ // Nargo generates session_id as a u64, which may exceed Number.MAX_SAFE_INTEGER.
43
+ // Zod 4's `.int()` enforces the safe-integer bound, so we drop it here and only require
44
+ // the value to be a non-negative number (it is used solely as a Map key).
69
45
  // eslint-disable-next-line camelcase
70
- session_id: z.number().int().nonnegative(),
46
+ session_id: z.number().nonnegative(),
71
47
  function: z.string() as z.ZodType<TXEOracleFunctionName>,
72
48
  // eslint-disable-next-line camelcase
73
49
  root_path: z.string(),
@@ -77,205 +53,101 @@ const TXEForeignCallInputSchema = zodFor<TXEForeignCallInput>()(
77
53
  }),
78
54
  );
79
55
 
80
- class TXEDispatcher {
81
- private contractStore!: ContractStore;
82
-
83
- constructor(private logger: Logger) {}
84
-
85
- private fastHashFile(path: string) {
86
- return new Promise(resolve => {
87
- const fd = createReadStream(path);
88
- const hash = createHash('sha1');
89
- hash.setEncoding('hex');
90
-
91
- fd.on('end', function () {
92
- hash.end();
93
- resolve(hash.read());
94
- });
95
-
96
- fd.pipe(hash);
97
- });
98
- }
99
-
100
- async #processDeployInputs({ inputs, root_path: rootPath, package_name: packageName }: TXEForeignCallInput) {
101
- const [contractPath, initializer] = inputs.slice(0, 2).map(input =>
102
- fromArray(input as ForeignCallArray)
103
- .map(char => String.fromCharCode(char.toNumber()))
104
- .join(''),
105
- );
106
-
107
- const decodedArgs = fromArray(inputs[3] as ForeignCallArray);
108
- const secret = fromSingle(inputs[4] as ForeignCallSingle);
109
- const salt = fromSingle(inputs[5] as ForeignCallSingle);
110
- const deployer = addressFromSingle(inputs[6] as ForeignCallSingle);
111
- const publicKeys = secret.equals(Fr.ZERO) ? PublicKeys.default() : (await deriveKeys(secret)).publicKeys;
112
- const publicKeysHash = await publicKeys.hash();
113
-
114
- let artifactPath = '';
115
- const { dir: contractDirectory, base: contractFilename } = parse(contractPath);
116
- if (contractDirectory) {
117
- if (contractDirectory.includes('@')) {
118
- // We're deploying a contract that belongs in a workspace
119
- // env.deploy("../path/to/workspace/root@packageName/contractName")
120
- const [workspace, pkg] = contractDirectory.split('@');
121
- const targetPath = join(rootPath, workspace, '/target');
122
- this.logger.debug(`Looking for compiled artifact in workspace ${targetPath}`);
123
- artifactPath = join(targetPath, `${pkg}-${contractFilename}.json`);
124
- } else {
125
- // We're deploying a standalone external contract
126
- // env.deploy("../path/to/contract/root/contractName")
127
- const targetPath = join(rootPath, contractDirectory, '/target');
128
- this.logger.debug(`Looking for compiled artifact in ${targetPath}`);
129
- [artifactPath] = (await readdir(targetPath)).filter(file => file.endsWith(`-${contractFilename}.json`));
130
- }
131
- } else {
132
- // We're deploying a local contract
133
- // env.deploy("contractName")
134
- artifactPath = join(rootPath, './target', `${packageName}-${contractFilename}.json`);
135
- }
136
-
137
- const fileHash = await this.fastHashFile(artifactPath);
138
-
139
- const cacheKey = `${contractDirectory ?? ''}-${contractFilename}-${initializer}-${decodedArgs
140
- .map(arg => arg.toString())
141
- .join('-')}-${publicKeysHash}-${salt}-${deployer}-${fileHash}`;
142
-
143
- let instance;
144
- let artifact: ContractArtifactWithHash;
145
-
146
- if (TXEArtifactsCache.has(cacheKey)) {
147
- this.logger.debug(`Using cached artifact for ${cacheKey}`);
148
- ({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
149
- } else {
150
- if (!TXEArtifactsCacheInFlight.has(cacheKey)) {
151
- this.logger.debug(`Loading compiled artifact ${artifactPath}`);
152
- const compute = async () => {
153
- const artifactJSON = JSON.parse(await readFile(artifactPath, 'utf-8')) as NoirCompiledContract;
154
- const artifactWithoutHash = loadContractArtifact(artifactJSON);
155
- const computedArtifact: ContractArtifactWithHash = {
156
- ...artifactWithoutHash,
157
- // Artifact hash is *very* expensive to compute, so we do it here once
158
- // and the TXE contract data provider can cache it
159
- artifactHash: await computeArtifactHash(artifactWithoutHash),
160
- };
161
- this.logger.debug(
162
- `Deploy ${computedArtifact.name} with initializer ${initializer}(${decodedArgs}) and public keys hash ${publicKeysHash.toString()}`,
163
- );
164
- const computedInstance = await getContractInstanceFromInstantiationParams(computedArtifact, {
165
- constructorArgs: decodedArgs,
166
- skipArgsDecoding: true,
167
- salt,
168
- publicKeys,
169
- constructorArtifact: initializer ? initializer : undefined,
170
- deployer,
171
- });
172
- const result = { artifact: computedArtifact, instance: computedInstance };
173
- TXEArtifactsCache.set(cacheKey, result);
174
- TXEArtifactsCacheInFlight.delete(cacheKey);
175
- return result;
176
- };
177
- TXEArtifactsCacheInFlight.set(cacheKey, compute());
178
- }
179
- ({ artifact, instance } = await TXEArtifactsCacheInFlight.get(cacheKey)!);
180
- }
56
+ export interface TXEDispatcherOptions {
57
+ /**
58
+ * Path to an LMDB directory holding the required protocol contracts (see
59
+ * {@link TXE_REQUIRED_PROTOCOL_CONTRACTS}) and the SchnorrAccount artifact. When set, the
60
+ * dispatcher clones this directory into a fresh tmpdir on first use instead of registering
61
+ * the contracts itself.
62
+ */
63
+ contractStoreSourceDir: string;
64
+ /**
65
+ * Class id (hex) of the SchnorrAccount artifact pre-registered in the shared LMDB. The
66
+ * {@link TXEArtifactResolver} looks the artifact up from the cloned store via this class id
67
+ * instead of recomputing it via `getSchnorrAccountContractArtifact()` + `computeArtifactHash()`.
68
+ */
69
+ schnorrClassId: string;
70
+ }
181
71
 
182
- inputs.splice(0, 1, artifact, instance, toSingle(secret));
72
+ export class TXEDispatcher {
73
+ private contractStore!: ContractStore;
74
+ private artifactResolver!: TXEArtifactResolver;
75
+ private readonly contractStoreSourceDir: string;
76
+ private readonly schnorrClassId: Fr;
77
+
78
+ constructor(
79
+ private logger: Logger,
80
+ opts: TXEDispatcherOptions,
81
+ ) {
82
+ this.contractStoreSourceDir = opts.contractStoreSourceDir;
83
+ this.schnorrClassId = Fr.fromString(opts.schnorrClassId);
183
84
  }
184
85
 
185
- async #processAddAccountInputs({ inputs }: TXEForeignCallInput) {
186
- const secret = fromSingle(inputs[0] as ForeignCallSingle);
187
-
188
- const cacheKey = `SchnorrAccountContract-${secret}`;
189
-
190
- let artifact: ContractArtifactWithHash;
191
- let instance;
192
-
193
- if (TXEArtifactsCache.has(cacheKey)) {
194
- this.logger.debug(`Using cached artifact for ${cacheKey}`);
195
- ({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
196
- } else {
197
- if (!TXEArtifactsCacheInFlight.has(cacheKey)) {
198
- const compute = async () => {
199
- const keys = await deriveKeys(secret);
200
- const args = [
201
- keys.publicKeys.masterIncomingViewingPublicKey.x,
202
- keys.publicKeys.masterIncomingViewingPublicKey.y,
203
- ];
204
- const computedArtifact: ContractArtifactWithHash = {
205
- ...SchnorrAccountContractArtifact,
206
- // Artifact hash is *very* expensive to compute, so we do it here once
207
- // and the TXE contract data provider can cache it
208
- artifactHash: await computeArtifactHash(SchnorrAccountContractArtifact),
209
- };
210
- const computedInstance = await getContractInstanceFromInstantiationParams(computedArtifact, {
211
- constructorArgs: args,
212
- skipArgsDecoding: true,
213
- salt: Fr.ONE,
214
- publicKeys: keys.publicKeys,
215
- constructorArtifact: 'constructor',
216
- deployer: AztecAddress.ZERO,
217
- });
218
- const result = { artifact: computedArtifact, instance: computedInstance };
219
- TXEArtifactsCache.set(cacheKey, result);
220
- TXEArtifactsCacheInFlight.delete(cacheKey);
221
- return result;
222
- };
223
- TXEArtifactsCacheInFlight.set(cacheKey, compute());
224
- }
225
- ({ artifact, instance } = await TXEArtifactsCacheInFlight.get(cacheKey)!);
86
+ /**
87
+ * Clones the pre-populated LMDB at `contractStoreSourceDir` into a fresh per-instance tmpdir
88
+ * on first use, so this dispatcher has a writable store already containing the required
89
+ * protocol contracts + SchnorrAccount. Idempotent — subsequent calls are no-ops.
90
+ */
91
+ private async warmUp(): Promise<void> {
92
+ if (this.contractStore) {
93
+ return;
226
94
  }
227
-
228
- inputs.splice(0, 0, artifact, instance);
95
+ const t0 = Date.now();
96
+ const kvStore = await cloneEphemeralStoreFrom(
97
+ join(this.contractStoreSourceDir, 'data.mdb'),
98
+ 'txe-contracts',
99
+ undefined,
100
+ 2,
101
+ );
102
+ this.contractStore = new ContractStore(kvStore);
103
+ this.artifactResolver = new TXEArtifactResolver(this.contractStore, this.schnorrClassId);
104
+ this.logger.debug('Cloned shared protocol-contracts store', { totalMs: Date.now() - t0 });
229
105
  }
230
106
 
231
107
  // eslint-disable-next-line camelcase
232
108
  async resolve_foreign_call(callData: TXEForeignCallInput): Promise<ForeignCallResult> {
233
- const { session_id: sessionId, function: functionName, inputs } = callData;
109
+ const {
110
+ session_id: sessionId,
111
+ function: functionName,
112
+ inputs,
113
+ root_path: rootPath,
114
+ package_name: packageName,
115
+ } = callData;
234
116
  this.logger.debug(`Calling ${functionName} on session ${sessionId}`);
235
117
 
236
118
  if (!sessions.has(sessionId)) {
237
119
  this.logger.debug(`Creating new session ${sessionId}`);
238
- if (!this.contractStore) {
239
- const kvStore = await openTmpStore('txe-contracts');
240
- this.contractStore = new ContractStore(kvStore);
241
- const provider = new BundledProtocolContractsProvider();
242
- for (const name of protocolContractNames) {
243
- const { instance, artifact } = await provider.getProtocolContractArtifact(name);
244
- await this.contractStore.addContractArtifact(artifact);
245
- await this.contractStore.addContractInstance(instance);
246
- }
247
- this.logger.debug('Registered protocol contracts in shared contract store');
248
- }
249
- sessions.set(sessionId, await TXESession.init(this.contractStore));
250
- }
251
-
252
- switch (functionName) {
253
- case 'aztec_txe_deploy': {
254
- await this.#processDeployInputs(callData);
255
- break;
256
- }
257
- case 'aztec_txe_addAccount': {
258
- await this.#processAddAccountInputs(callData);
259
- break;
260
- }
120
+ await this.warmUp();
121
+ sessions.set(sessionId, await TXESession.init(this.contractStore, this.artifactResolver, rootPath, packageName));
261
122
  }
262
123
 
263
124
  return await sessions.get(sessionId)!.processFunction(functionName, inputs);
264
125
  }
126
+
127
+ /**
128
+ * Releases a session and its resources (per-session LMDB + `NativeWorldStateService`).
129
+ * Called by the dispatcher pool when nargo closes its TCP connection for a test (see
130
+ * `rpc_server.ts`'s socket tracker). No-op if the session was never created — that happens
131
+ * when nargo opens a connection but errors before sending a request.
132
+ */
133
+ async disposeSession(sessionId: number): Promise<void> {
134
+ const session = sessions.get(sessionId);
135
+ if (!session) {
136
+ return;
137
+ }
138
+ sessions.delete(sessionId);
139
+ await session.dispose();
140
+ }
265
141
  }
266
142
 
267
- const TXEDispatcherApiSchema: ApiSchemaFor<TXEDispatcher> = {
143
+ /** Diagnostic-only: number of sessions currently held by this worker. */
144
+ export function activeSessionCount(): number {
145
+ return sessions.size;
146
+ }
147
+
148
+ export const TXEDispatcherApiSchema: ApiSchemaFor<TXEDispatcher> = {
268
149
  // eslint-disable-next-line camelcase
269
- resolve_foreign_call: z.function().args(TXEForeignCallInputSchema).returns(ForeignCallResultSchema),
150
+ resolve_foreign_call: z.function({ input: z.tuple([TXEForeignCallInputSchema]), output: ForeignCallResultSchema }),
151
+ // disposeSession is invoked over IPC from the worker, not via RPC; required by ApiSchemaFor.
152
+ disposeSession: z.function({ input: z.tuple([z.number().nonnegative()]), output: z.void() }),
270
153
  };
271
-
272
- /**
273
- * Creates an RPC server that forwards calls to the TXE.
274
- * @param logger - Logger to output to
275
- * @returns A TXE RPC server.
276
- */
277
- export function createTXERpcServer(logger: Logger) {
278
- return createSafeJsonRpcServer(new TXEDispatcher(logger), TXEDispatcherApiSchema, {
279
- http200OnError: true,
280
- });
281
- }
@@ -0,0 +1,23 @@
1
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
+
3
+ import { addExtension } from 'msgpackr';
4
+
5
+ // `@aztec/native/msgpack_channel` registers a msgpackr extension that packs `Fr` instances to
6
+ // their raw 32-byte buffer for the C++ world-state side to deserialize. Because `@aztec/native`
7
+ // is externalized in TXE's esbuild config (it loads a `.node` binary that can't be bundled),
8
+ // the `Fr` class the extension is keyed on belongs to the *external* `@aztec/foundation`
9
+ // module instance — a different class identity than the `Fr` bundled into the TXE worker /
10
+ // bin. msgpackr's extension match uses `instanceof`, so bundled `Fr` instances slip through
11
+ // and msgpackr falls back to `Fr.toJSON()` (returns `Fr.toString()` — a `0x...` hex string).
12
+ // The C++ side then sees a string where a 32-byte binary was expected and throws
13
+ // `msgpack::type_error` whose `what()` is literally `"std::bad_cast"`.
14
+ //
15
+ // Fix: register the same extension under the BUNDLED `Fr` class identity. msgpackr's extension
16
+ // table holds an entry per `Class`, so both Fr classes now resolve to the same `write` callback
17
+ // and produce the same wire format. Imported as a side-effect from `./index.js` so both the
18
+ // worker (via `./worker.ts`) and the main-thread RPC server (via `./rpc_server.ts`) get the
19
+ // registration before any `sendMessage` runs.
20
+ addExtension({
21
+ Class: Fr,
22
+ write: (fr: Fr) => fr.toBuffer(),
23
+ });
@@ -1,11 +1,14 @@
1
- import type { ContractArtifact } from '@aztec/aztec.js/abi';
2
1
  import { CompleteAddress } from '@aztec/aztec.js/addresses';
3
- import type { ContractInstanceWithAddress } from '@aztec/aztec.js/contracts';
4
2
  import { TxHash } from '@aztec/aztec.js/tx';
5
3
  import { BlockNumber } from '@aztec/foundation/branded-types';
6
4
  import type { Fr } from '@aztec/foundation/curves/bn254';
5
+ import type { EthAddress } from '@aztec/foundation/eth-address';
6
+ import type { TaggingSecretStrategy } from '@aztec/pxe/server';
7
+ import type { Option } from '@aztec/pxe/simulator';
7
8
  import type { EventSelector, FunctionSelector } from '@aztec/stdlib/abi';
8
9
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
10
+ import type { GasSettings } from '@aztec/stdlib/gas';
11
+ import type { PrivateLog } from '@aztec/stdlib/logs';
9
12
  import type { UInt64 } from '@aztec/stdlib/types';
10
13
 
11
14
  // These interfaces complement the ones defined in PXE, and combined with those contain the full list of oracles used by
@@ -36,6 +39,15 @@ export interface IAvmExecutionOracle {
36
39
  nullifierExists(siloedNullifier: Fr): Promise<boolean>;
37
40
  storageWrite(slot: Fr, value: Fr): Promise<void>;
38
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>;
39
51
  }
40
52
 
41
53
  /**
@@ -49,37 +61,64 @@ export interface ITxeExecutionOracle {
49
61
  getNextBlockTimestamp(): Promise<UInt64>;
50
62
  advanceBlocksBy(blocks: number): Promise<void>;
51
63
  advanceTimestampBy(duration: UInt64): void;
52
- deploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, foreignSecret: Fr): Promise<void>;
53
- createAccount(secret: Fr): Promise<CompleteAddress>;
54
- addAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr): Promise<CompleteAddress>;
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>;
55
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;
56
88
  getLastBlockTimestamp(): Promise<bigint>;
57
89
  getLastTxEffects(): Promise<{
58
90
  txHash: TxHash;
59
91
  noteHashes: Fr[];
60
92
  nullifiers: Fr[];
93
+ privateLogs: PrivateLog[];
61
94
  }>;
62
95
  getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress): Promise<Fr[][]>;
63
96
  privateCallNewFlow(
64
- from: AztecAddress,
97
+ from: AztecAddress | undefined,
65
98
  targetContractAddress: AztecAddress,
66
99
  functionSelector: FunctionSelector,
67
100
  args: Fr[],
68
101
  argsHash: Fr,
69
102
  isStaticCall: boolean,
103
+ additionalScopes: AztecAddress[],
70
104
  jobId: string,
71
- ): Promise<Fr[]>;
105
+ authorizedUtilityCallTargets: AztecAddress[],
106
+ gasSettings: GasSettings,
107
+ ): Promise<{ returnValues: Fr[]; offchainEffects: Fr[][] }>;
72
108
  executeUtilityFunction(
109
+ from: AztecAddress | undefined,
73
110
  targetContractAddress: AztecAddress,
74
111
  functionSelector: FunctionSelector,
75
112
  args: Fr[],
76
113
  jobId: string,
114
+ authorizedUtilityCallTargets: AztecAddress[],
77
115
  ): Promise<Fr[]>;
78
116
  publicCallNewFlow(
79
- from: AztecAddress,
117
+ from: AztecAddress | undefined,
80
118
  targetContractAddress: AztecAddress,
81
119
  calldata: Fr[],
82
120
  isStaticCall: boolean,
121
+ gasSettings: GasSettings,
83
122
  ): Promise<Fr[]>;
84
123
  // TODO(F-335): Drop this from here as it's not a real oracle handler - it's only called from
85
124
  // RPCTranslator::txeGetPrivateEvents and never from Noir.
@@ -0,0 +1,25 @@
1
+ import {
2
+ DEFAULT_TAGGING_SECRET_STRATEGY,
3
+ type ResolveTaggingSecretStrategy,
4
+ type TaggingSecretStrategy,
5
+ } from '@aztec/pxe/server';
6
+ import type { AppTaggingSecretKind } from '@aztec/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
+ }