@aztec/txe 0.0.1-commit.e558bd1c → 0.0.1-commit.e57c76e
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-PII7Q556.js +3 -0
- package/dest/ContractClassRegistry-PII7Q556.js.map +7 -0
- package/dest/ContractInstanceRegistry-3MHLGFNZ.js +3 -0
- package/dest/ContractInstanceRegistry-3MHLGFNZ.js.map +7 -0
- package/dest/FeeJuice-TQV5AQK7.js +3 -0
- package/dest/FeeJuice-TQV5AQK7.js.map +7 -0
- package/dest/MultiCallEntrypoint-SBJUIJY7.js +3 -0
- package/dest/MultiCallEntrypoint-SBJUIJY7.js.map +7 -0
- package/dest/SchnorrAccount-MQXPIZ44.js +3 -0
- package/dest/SchnorrAccount-MQXPIZ44.js.map +7 -0
- package/dest/bin/check_txe_oracle_version.d.ts +2 -0
- package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
- package/dest/bin/check_txe_oracle_version.js +61 -0
- package/dest/bin/index.js +3 -30
- package/dest/bin/index.js.map +7 -0
- package/dest/bin/oracle_test_server.d.ts +3 -0
- package/dest/bin/oracle_test_server.d.ts.map +1 -0
- package/dest/bin/oracle_test_server.js +41 -0
- package/dest/chunk-3VX4QA7B.js +265 -0
- package/dest/chunk-3VX4QA7B.js.map +7 -0
- package/dest/chunk-BJVAAXNA.js +3 -0
- package/dest/chunk-BJVAAXNA.js.map +7 -0
- package/dest/chunk-FU3LBVE4.js +304 -0
- package/dest/chunk-FU3LBVE4.js.map +7 -0
- package/dest/constants.d.ts +5 -1
- package/dest/constants.d.ts.map +1 -1
- package/dest/constants.js +8 -0
- package/dest/dispatcher_pool.d.ts +67 -0
- package/dest/dispatcher_pool.d.ts.map +1 -0
- package/dest/dispatcher_pool.js +284 -0
- package/dest/index.d.ts +185 -7
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +71 -157
- package/dest/metafile.json +38352 -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 +56 -30
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
- package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
- package/dest/oracle/test-resolver/fixtures.js +39 -0
- package/dest/oracle/test-resolver/index.d.ts +9 -0
- package/dest/oracle/test-resolver/index.d.ts.map +1 -0
- package/dest/oracle/test-resolver/index.js +33 -0
- package/dest/oracle/test-resolver/resolver.d.ts +34 -0
- package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
- package/dest/oracle/test-resolver/resolver.js +114 -0
- package/dest/oracle/txe_oracle_public_context.d.ts +38 -14
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +55 -13
- package/dest/oracle/txe_oracle_registry.d.ts +147 -0
- package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_registry.js +562 -0
- package/dest/oracle/txe_oracle_top_level_context.d.ts +45 -26
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +241 -85
- 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 +96 -228
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +815 -624
- package/dest/server.bundle.js +3 -0
- package/dest/server.bundle.js.map +7 -0
- package/dest/state_machine/archiver.d.ts +6 -5
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +30 -21
- package/dest/state_machine/dummy_p2p_client.d.ts +24 -15
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +39 -18
- 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 +33 -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_session.d.ts +80 -15
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +236 -42
- package/dest/utils/encoding.d.ts +191 -0
- package/dest/utils/encoding.d.ts.map +1 -0
- package/dest/{util → utils}/encoding.js +7 -2
- package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
- package/dest/utils/expected_failure_error.d.ts.map +1 -0
- package/dest/{util → utils}/txe_account_store.d.ts +1 -1
- package/dest/utils/txe_account_store.d.ts.map +1 -0
- package/dest/utils/txe_artifact_resolver.d.ts +37 -0
- package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
- package/dest/utils/txe_artifact_resolver.js +161 -0
- package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
- package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
- package/dest/{util → utils}/txe_public_contract_data_source.js +6 -25
- package/dest/worker.bundle.js +3 -0
- package/dest/worker.bundle.js.map +7 -0
- package/dest/worker.d.ts +2 -0
- package/dest/worker.d.ts.map +1 -0
- package/dest/worker.js +92 -0
- package/package.json +38 -21
- package/src/bin/check_txe_oracle_version.ts +70 -0
- package/src/bin/index.ts +11 -2
- package/src/bin/oracle_test_server.ts +51 -0
- package/src/constants.ts +10 -0
- package/src/dispatcher_pool.ts +316 -0
- package/src/index.ts +97 -190
- package/src/msgpackr_fr_extension.ts +23 -0
- package/src/oracle/interfaces.ts +58 -35
- package/src/oracle/test-resolver/fixtures.ts +84 -0
- package/src/oracle/test-resolver/index.ts +45 -0
- package/src/oracle/test-resolver/resolver.ts +165 -0
- package/src/oracle/txe_oracle_public_context.ts +72 -12
- package/src/oracle/txe_oracle_registry.ts +401 -0
- package/src/oracle/txe_oracle_top_level_context.ts +279 -144
- package/src/oracle/txe_oracle_version.ts +17 -0
- package/src/oracle/txe_private_execution_oracle.ts +30 -0
- package/src/rpc_server.ts +87 -0
- package/src/rpc_translator.ts +1000 -1006
- package/src/state_machine/archiver.ts +42 -20
- package/src/state_machine/dummy_p2p_client.ts +63 -30
- package/src/state_machine/global_variable_builder.ts +18 -3
- package/src/state_machine/index.ts +25 -5
- package/src/state_machine/mock_epoch_cache.ts +43 -3
- package/src/state_machine/synchronizer.ts +5 -5
- package/src/txe_session.ts +351 -96
- package/src/{util → utils}/encoding.ts +8 -2
- package/src/utils/txe_artifact_resolver.ts +217 -0
- package/src/{util → utils}/txe_public_contract_data_source.ts +10 -38
- 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_contract_store.d.ts +0 -12
- package/dest/util/txe_contract_store.d.ts.map +0 -1
- package/dest/util/txe_contract_store.js +0 -22
- package/dest/util/txe_public_contract_data_source.d.ts +0 -21
- package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
- package/src/util/txe_contract_store.ts +0 -36
- /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
package/src/index.ts
CHANGED
|
@@ -1,52 +1,33 @@
|
|
|
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 {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
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';
|
|
15
6
|
import type { ApiSchemaFor } from '@aztec/stdlib/schemas';
|
|
16
7
|
import { zodFor } from '@aztec/stdlib/schemas';
|
|
17
8
|
|
|
18
|
-
import {
|
|
19
|
-
import { createReadStream } from 'fs';
|
|
20
|
-
import { readFile, readdir } from 'fs/promises';
|
|
21
|
-
import { join, parse } from 'path';
|
|
9
|
+
import { join } from 'path';
|
|
22
10
|
import { z } from 'zod';
|
|
23
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';
|
|
24
15
|
import { type TXEOracleFunctionName, TXESession } from './txe_session.js';
|
|
25
16
|
import {
|
|
26
17
|
type ForeignCallArgs,
|
|
27
18
|
ForeignCallArgsSchema,
|
|
28
|
-
type ForeignCallArray,
|
|
29
19
|
type ForeignCallResult,
|
|
30
20
|
ForeignCallResultSchema,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
fromSingle,
|
|
34
|
-
toSingle,
|
|
35
|
-
} from './util/encoding.js';
|
|
36
|
-
import type { ContractArtifactWithHash } from './util/txe_contract_store.js';
|
|
21
|
+
} from './utils/encoding.js';
|
|
22
|
+
import { TXEArtifactResolver } from './utils/txe_artifact_resolver.js';
|
|
37
23
|
|
|
38
|
-
|
|
24
|
+
// Protocol contracts TXE registers in its contract store. Only AuthRegistry is needed for the
|
|
25
|
+
// current test suites; add a contract here if a lookup against a `0x000…00X` address fails.
|
|
26
|
+
export const TXE_REQUIRED_PROTOCOL_CONTRACTS: ProtocolContractName[] = [];
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
* TXE typically has to load the same contract artifacts over and over again for multiple tests,
|
|
42
|
-
* so we cache them here to avoid both loading them from disk repeatedly and computing their artifact hashes
|
|
43
|
-
*/
|
|
44
|
-
const TXEArtifactsCache = new Map<
|
|
45
|
-
string,
|
|
46
|
-
{ artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }
|
|
47
|
-
>();
|
|
28
|
+
const sessions = new Map<number, TXESession>();
|
|
48
29
|
|
|
49
|
-
type TXEForeignCallInput = {
|
|
30
|
+
export type TXEForeignCallInput = {
|
|
50
31
|
session_id: number;
|
|
51
32
|
function: TXEOracleFunctionName;
|
|
52
33
|
root_path: string;
|
|
@@ -54,10 +35,13 @@ type TXEForeignCallInput = {
|
|
|
54
35
|
inputs: ForeignCallArgs;
|
|
55
36
|
};
|
|
56
37
|
|
|
57
|
-
const TXEForeignCallInputSchema = zodFor<TXEForeignCallInput>()(
|
|
38
|
+
export const TXEForeignCallInputSchema = zodFor<TXEForeignCallInput>()(
|
|
58
39
|
z.object({
|
|
40
|
+
// Nargo generates session_id as a u64, which may exceed Number.MAX_SAFE_INTEGER.
|
|
41
|
+
// Zod 4's `.int()` enforces the safe-integer bound, so we drop it here and only require
|
|
42
|
+
// the value to be a non-negative number (it is used solely as a Map key).
|
|
59
43
|
// eslint-disable-next-line camelcase
|
|
60
|
-
session_id: z.number().
|
|
44
|
+
session_id: z.number().nonnegative(),
|
|
61
45
|
function: z.string() as z.ZodType<TXEOracleFunctionName>,
|
|
62
46
|
// eslint-disable-next-line camelcase
|
|
63
47
|
root_path: z.string(),
|
|
@@ -67,178 +51,101 @@ const TXEForeignCallInputSchema = zodFor<TXEForeignCallInput>()(
|
|
|
67
51
|
}),
|
|
68
52
|
);
|
|
69
53
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
fd.pipe(hash);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async #processDeployInputs({ inputs, root_path: rootPath, package_name: packageName }: TXEForeignCallInput) {
|
|
91
|
-
const [contractPath, initializer] = inputs.slice(0, 2).map(input =>
|
|
92
|
-
fromArray(input as ForeignCallArray)
|
|
93
|
-
.map(char => String.fromCharCode(char.toNumber()))
|
|
94
|
-
.join(''),
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
const decodedArgs = fromArray(inputs[3] as ForeignCallArray);
|
|
98
|
-
const secret = fromSingle(inputs[4] as ForeignCallSingle);
|
|
99
|
-
const publicKeys = secret.equals(Fr.ZERO) ? PublicKeys.default() : (await deriveKeys(secret)).publicKeys;
|
|
100
|
-
const publicKeysHash = await publicKeys.hash();
|
|
101
|
-
|
|
102
|
-
let artifactPath = '';
|
|
103
|
-
const { dir: contractDirectory, base: contractFilename } = parse(contractPath);
|
|
104
|
-
if (contractDirectory) {
|
|
105
|
-
if (contractDirectory.includes('@')) {
|
|
106
|
-
// We're deploying a contract that belongs in a workspace
|
|
107
|
-
// env.deploy("../path/to/workspace/root@packageName/contractName")
|
|
108
|
-
const [workspace, pkg] = contractDirectory.split('@');
|
|
109
|
-
const targetPath = join(rootPath, workspace, '/target');
|
|
110
|
-
this.logger.debug(`Looking for compiled artifact in workspace ${targetPath}`);
|
|
111
|
-
artifactPath = join(targetPath, `${pkg}-${contractFilename}.json`);
|
|
112
|
-
} else {
|
|
113
|
-
// We're deploying a standalone external contract
|
|
114
|
-
// env.deploy("../path/to/contract/root/contractName")
|
|
115
|
-
const targetPath = join(rootPath, contractDirectory, '/target');
|
|
116
|
-
this.logger.debug(`Looking for compiled artifact in ${targetPath}`);
|
|
117
|
-
[artifactPath] = (await readdir(targetPath)).filter(file => file.endsWith(`-${contractFilename}.json`));
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
// We're deploying a local contract
|
|
121
|
-
// env.deploy("contractName")
|
|
122
|
-
artifactPath = join(rootPath, './target', `${packageName}-${contractFilename}.json`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const fileHash = await this.fastHashFile(artifactPath);
|
|
126
|
-
|
|
127
|
-
const cacheKey = `${contractDirectory ?? ''}-${contractFilename}-${initializer}-${decodedArgs
|
|
128
|
-
.map(arg => arg.toString())
|
|
129
|
-
.join('-')}-${publicKeysHash}-${fileHash}`;
|
|
130
|
-
|
|
131
|
-
let instance;
|
|
132
|
-
let artifact: ContractArtifactWithHash;
|
|
133
|
-
|
|
134
|
-
if (TXEArtifactsCache.has(cacheKey)) {
|
|
135
|
-
this.logger.debug(`Using cached artifact for ${cacheKey}`);
|
|
136
|
-
({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
|
|
137
|
-
} else {
|
|
138
|
-
this.logger.debug(`Loading compiled artifact ${artifactPath}`);
|
|
139
|
-
const artifactJSON = JSON.parse(await readFile(artifactPath, 'utf-8')) as NoirCompiledContract;
|
|
140
|
-
const artifactWithoutHash = loadContractArtifact(artifactJSON);
|
|
141
|
-
artifact = {
|
|
142
|
-
...artifactWithoutHash,
|
|
143
|
-
// Artifact hash is *very* expensive to compute, so we do it here once
|
|
144
|
-
// and the TXE contract data provider can cache it
|
|
145
|
-
artifactHash: await computeArtifactHash(artifactWithoutHash),
|
|
146
|
-
};
|
|
147
|
-
this.logger.debug(
|
|
148
|
-
`Deploy ${
|
|
149
|
-
artifact.name
|
|
150
|
-
} with initializer ${initializer}(${decodedArgs}) and public keys hash ${publicKeysHash.toString()}`,
|
|
151
|
-
);
|
|
152
|
-
instance = await getContractInstanceFromInstantiationParams(artifact, {
|
|
153
|
-
constructorArgs: decodedArgs,
|
|
154
|
-
skipArgsDecoding: true,
|
|
155
|
-
salt: Fr.ONE,
|
|
156
|
-
publicKeys,
|
|
157
|
-
constructorArtifact: initializer ? initializer : undefined,
|
|
158
|
-
deployer: AztecAddress.ZERO,
|
|
159
|
-
});
|
|
160
|
-
TXEArtifactsCache.set(cacheKey, { artifact, instance });
|
|
161
|
-
}
|
|
54
|
+
export interface TXEDispatcherOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Path to an LMDB directory holding the required protocol contracts (see
|
|
57
|
+
* {@link TXE_REQUIRED_PROTOCOL_CONTRACTS}) and the SchnorrAccount artifact. When set, the
|
|
58
|
+
* dispatcher clones this directory into a fresh tmpdir on first use instead of registering
|
|
59
|
+
* the contracts itself.
|
|
60
|
+
*/
|
|
61
|
+
contractStoreSourceDir: string;
|
|
62
|
+
/**
|
|
63
|
+
* Class id (hex) of the SchnorrAccount artifact pre-registered in the shared LMDB. The
|
|
64
|
+
* {@link TXEArtifactResolver} looks the artifact up from the cloned store via this class id
|
|
65
|
+
* instead of recomputing it via `getSchnorrAccountContractArtifact()` + `computeArtifactHash()`.
|
|
66
|
+
*/
|
|
67
|
+
schnorrClassId: string;
|
|
68
|
+
}
|
|
162
69
|
|
|
163
|
-
|
|
70
|
+
export class TXEDispatcher {
|
|
71
|
+
private contractStore!: ContractStore;
|
|
72
|
+
private artifactResolver!: TXEArtifactResolver;
|
|
73
|
+
private readonly contractStoreSourceDir: string;
|
|
74
|
+
private readonly schnorrClassId: Fr;
|
|
75
|
+
|
|
76
|
+
constructor(
|
|
77
|
+
private logger: Logger,
|
|
78
|
+
opts: TXEDispatcherOptions,
|
|
79
|
+
) {
|
|
80
|
+
this.contractStoreSourceDir = opts.contractStoreSourceDir;
|
|
81
|
+
this.schnorrClassId = Fr.fromString(opts.schnorrClassId);
|
|
164
82
|
}
|
|
165
83
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (TXEArtifactsCache.has(cacheKey)) {
|
|
175
|
-
this.logger.debug(`Using cached artifact for ${cacheKey}`);
|
|
176
|
-
({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
|
|
177
|
-
} else {
|
|
178
|
-
const keys = await deriveKeys(secret);
|
|
179
|
-
const args = [keys.publicKeys.masterIncomingViewingPublicKey.x, keys.publicKeys.masterIncomingViewingPublicKey.y];
|
|
180
|
-
artifact = {
|
|
181
|
-
...SchnorrAccountContractArtifact,
|
|
182
|
-
// Artifact hash is *very* expensive to compute, so we do it here once
|
|
183
|
-
// and the TXE contract data provider can cache it
|
|
184
|
-
artifactHash: await computeArtifactHash(SchnorrAccountContractArtifact),
|
|
185
|
-
};
|
|
186
|
-
instance = await getContractInstanceFromInstantiationParams(artifact, {
|
|
187
|
-
constructorArgs: args,
|
|
188
|
-
skipArgsDecoding: true,
|
|
189
|
-
salt: Fr.ONE,
|
|
190
|
-
publicKeys: keys.publicKeys,
|
|
191
|
-
constructorArtifact: 'constructor',
|
|
192
|
-
deployer: AztecAddress.ZERO,
|
|
193
|
-
});
|
|
194
|
-
TXEArtifactsCache.set(cacheKey, { artifact, instance });
|
|
84
|
+
/**
|
|
85
|
+
* Clones the pre-populated LMDB at `contractStoreSourceDir` into a fresh per-instance tmpdir
|
|
86
|
+
* on first use, so this dispatcher has a writable store already containing the required
|
|
87
|
+
* protocol contracts + SchnorrAccount. Idempotent — subsequent calls are no-ops.
|
|
88
|
+
*/
|
|
89
|
+
private async warmUp(): Promise<void> {
|
|
90
|
+
if (this.contractStore) {
|
|
91
|
+
return;
|
|
195
92
|
}
|
|
196
|
-
|
|
197
|
-
|
|
93
|
+
const t0 = Date.now();
|
|
94
|
+
const kvStore = await cloneEphemeralStoreFrom(
|
|
95
|
+
join(this.contractStoreSourceDir, 'data.mdb'),
|
|
96
|
+
'txe-contracts',
|
|
97
|
+
undefined,
|
|
98
|
+
2,
|
|
99
|
+
);
|
|
100
|
+
this.contractStore = new ContractStore(kvStore);
|
|
101
|
+
this.artifactResolver = new TXEArtifactResolver(this.contractStore, this.schnorrClassId);
|
|
102
|
+
this.logger.debug('Cloned shared protocol-contracts store', { totalMs: Date.now() - t0 });
|
|
198
103
|
}
|
|
199
104
|
|
|
200
105
|
// eslint-disable-next-line camelcase
|
|
201
106
|
async resolve_foreign_call(callData: TXEForeignCallInput): Promise<ForeignCallResult> {
|
|
202
|
-
const {
|
|
107
|
+
const {
|
|
108
|
+
session_id: sessionId,
|
|
109
|
+
function: functionName,
|
|
110
|
+
inputs,
|
|
111
|
+
root_path: rootPath,
|
|
112
|
+
package_name: packageName,
|
|
113
|
+
} = callData;
|
|
203
114
|
this.logger.debug(`Calling ${functionName} on session ${sessionId}`);
|
|
204
115
|
|
|
205
116
|
if (!sessions.has(sessionId)) {
|
|
206
117
|
this.logger.debug(`Creating new session ${sessionId}`);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
protocolContractNames.map(name => new BundledProtocolContractsProvider().getProtocolContractArtifact(name)),
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
sessions.set(sessionId, await TXESession.init(this.protocolContracts));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
switch (functionName) {
|
|
216
|
-
case 'txeDeploy': {
|
|
217
|
-
await this.#processDeployInputs(callData);
|
|
218
|
-
break;
|
|
219
|
-
}
|
|
220
|
-
case 'txeAddAccount': {
|
|
221
|
-
await this.#processAddAccountInputs(callData);
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
118
|
+
await this.warmUp();
|
|
119
|
+
sessions.set(sessionId, await TXESession.init(this.contractStore, this.artifactResolver, rootPath, packageName));
|
|
224
120
|
}
|
|
225
121
|
|
|
226
122
|
return await sessions.get(sessionId)!.processFunction(functionName, inputs);
|
|
227
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Releases a session and its resources (per-session LMDB + `NativeWorldStateService`).
|
|
127
|
+
* Called by the dispatcher pool when nargo closes its TCP connection for a test (see
|
|
128
|
+
* `rpc_server.ts`'s socket tracker). No-op if the session was never created — that happens
|
|
129
|
+
* when nargo opens a connection but errors before sending a request.
|
|
130
|
+
*/
|
|
131
|
+
async disposeSession(sessionId: number): Promise<void> {
|
|
132
|
+
const session = sessions.get(sessionId);
|
|
133
|
+
if (!session) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
sessions.delete(sessionId);
|
|
137
|
+
await session.dispose();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Diagnostic-only: number of sessions currently held by this worker. */
|
|
142
|
+
export function activeSessionCount(): number {
|
|
143
|
+
return sessions.size;
|
|
228
144
|
}
|
|
229
145
|
|
|
230
|
-
const TXEDispatcherApiSchema: ApiSchemaFor<TXEDispatcher> = {
|
|
146
|
+
export const TXEDispatcherApiSchema: ApiSchemaFor<TXEDispatcher> = {
|
|
231
147
|
// eslint-disable-next-line camelcase
|
|
232
|
-
resolve_foreign_call: z.function(
|
|
148
|
+
resolve_foreign_call: z.function({ input: z.tuple([TXEForeignCallInputSchema]), output: ForeignCallResultSchema }),
|
|
149
|
+
// disposeSession is invoked over IPC from the worker, not via RPC; required by ApiSchemaFor.
|
|
150
|
+
disposeSession: z.function({ input: z.tuple([z.number().nonnegative()]), output: z.void() }),
|
|
233
151
|
};
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Creates an RPC server that forwards calls to the TXE.
|
|
237
|
-
* @param logger - Logger to output to
|
|
238
|
-
* @returns A TXE RPC server.
|
|
239
|
-
*/
|
|
240
|
-
export function createTXERpcServer(logger: Logger) {
|
|
241
|
-
return createSafeJsonRpcServer(new TXEDispatcher(logger), TXEDispatcherApiSchema, {
|
|
242
|
-
http200OnError: true,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
@@ -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
|
+
});
|
package/src/oracle/interfaces.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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';
|
|
7
5
|
import type { EventSelector, FunctionSelector } from '@aztec/stdlib/abi';
|
|
8
6
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
7
|
+
import type { GasSettings } from '@aztec/stdlib/gas';
|
|
8
|
+
import type { PrivateLog } from '@aztec/stdlib/logs';
|
|
9
9
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
10
10
|
|
|
11
11
|
// These interfaces complement the ones defined in PXE, and combined with those contain the full list of oracles used by
|
|
@@ -24,18 +24,27 @@ import type { UInt64 } from '@aztec/stdlib/types';
|
|
|
24
24
|
export interface IAvmExecutionOracle {
|
|
25
25
|
isAvm: true;
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
address(): Promise<AztecAddress>;
|
|
28
|
+
sender(): Promise<AztecAddress>;
|
|
29
|
+
blockNumber(): Promise<BlockNumber>;
|
|
30
|
+
timestamp(): Promise<bigint>;
|
|
31
|
+
isStaticCall(): Promise<boolean>;
|
|
32
|
+
chainId(): Promise<Fr>;
|
|
33
|
+
version(): Promise<Fr>;
|
|
34
|
+
emitNullifier(nullifier: Fr): Promise<void>;
|
|
35
|
+
emitNoteHash(noteHash: Fr): Promise<void>;
|
|
36
|
+
nullifierExists(siloedNullifier: Fr): Promise<boolean>;
|
|
37
|
+
storageWrite(slot: Fr, value: Fr): Promise<void>;
|
|
38
|
+
storageRead(slot: Fr, contractAddress: AztecAddress): Promise<Fr>;
|
|
39
|
+
getContractInstanceDeployer(address: AztecAddress): Promise<{ member: Fr; exists: boolean }>;
|
|
40
|
+
getContractInstanceClassId(address: AztecAddress): Promise<{ member: Fr; exists: boolean }>;
|
|
41
|
+
getContractInstanceInitializationHash(address: AztecAddress): Promise<{ member: Fr; exists: boolean }>;
|
|
42
|
+
getContractInstanceImmutablesHash(address: AztecAddress): Promise<{ member: Fr; exists: boolean }>;
|
|
43
|
+
returndataSize(): Promise<Fr>;
|
|
44
|
+
returndataCopy(rdOffset: number, copySize: number): Promise<Fr[]>;
|
|
45
|
+
call(l2Gas: number, daGas: number, address: AztecAddress, argsLength: number, args: Fr[]): Promise<Fr[]>;
|
|
46
|
+
staticCall(l2Gas: number, daGas: number, address: AztecAddress, argsLength: number, args: Fr[]): Promise<Fr[]>;
|
|
47
|
+
successCopy(): Promise<Fr>;
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
/**
|
|
@@ -44,43 +53,57 @@ export interface IAvmExecutionOracle {
|
|
|
44
53
|
export interface ITxeExecutionOracle {
|
|
45
54
|
isTxe: true;
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
instance: ContractInstanceWithAddress,
|
|
56
|
+
getDefaultAddress(): AztecAddress;
|
|
57
|
+
getNextBlockNumber(): Promise<BlockNumber>;
|
|
58
|
+
getNextBlockTimestamp(): Promise<UInt64>;
|
|
59
|
+
advanceBlocksBy(blocks: number): Promise<void>;
|
|
60
|
+
advanceTimestampBy(duration: UInt64): void;
|
|
61
|
+
deploy(
|
|
62
|
+
contractPath: string,
|
|
63
|
+
initializer: string,
|
|
64
|
+
args: Fr[],
|
|
57
65
|
secret: Fr,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
salt: Fr,
|
|
67
|
+
deployer: AztecAddress,
|
|
68
|
+
): Promise<Fr[]>;
|
|
69
|
+
createAccount(secret: Fr): Promise<CompleteAddress>;
|
|
70
|
+
addAccount(secret: Fr): Promise<CompleteAddress>;
|
|
71
|
+
addAuthWitness(address: AztecAddress, messageHash: Fr): Promise<void>;
|
|
72
|
+
getLastBlockTimestamp(): Promise<bigint>;
|
|
73
|
+
getLastTxEffects(): Promise<{
|
|
62
74
|
txHash: TxHash;
|
|
63
75
|
noteHashes: Fr[];
|
|
64
76
|
nullifiers: Fr[];
|
|
77
|
+
privateLogs: PrivateLog[];
|
|
65
78
|
}>;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
from: AztecAddress,
|
|
79
|
+
getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress): Promise<Fr[][]>;
|
|
80
|
+
privateCallNewFlow(
|
|
81
|
+
from: AztecAddress | undefined,
|
|
69
82
|
targetContractAddress: AztecAddress,
|
|
70
83
|
functionSelector: FunctionSelector,
|
|
71
84
|
args: Fr[],
|
|
72
85
|
argsHash: Fr,
|
|
73
86
|
isStaticCall: boolean,
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
additionalScopes: AztecAddress[],
|
|
88
|
+
jobId: string,
|
|
89
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
90
|
+
gasSettings: GasSettings,
|
|
91
|
+
): Promise<{ returnValues: Fr[]; offchainEffects: Fr[][] }>;
|
|
92
|
+
executeUtilityFunction(
|
|
76
93
|
targetContractAddress: AztecAddress,
|
|
77
94
|
functionSelector: FunctionSelector,
|
|
78
95
|
args: Fr[],
|
|
96
|
+
jobId: string,
|
|
97
|
+
authorizedUtilityCallTargets: AztecAddress[],
|
|
79
98
|
): Promise<Fr[]>;
|
|
80
|
-
|
|
81
|
-
from: AztecAddress,
|
|
99
|
+
publicCallNewFlow(
|
|
100
|
+
from: AztecAddress | undefined,
|
|
82
101
|
targetContractAddress: AztecAddress,
|
|
83
102
|
calldata: Fr[],
|
|
84
103
|
isStaticCall: boolean,
|
|
104
|
+
gasSettings: GasSettings,
|
|
85
105
|
): Promise<Fr[]>;
|
|
106
|
+
// TODO(F-335): Drop this from here as it's not a real oracle handler - it's only called from
|
|
107
|
+
// RPCTranslator::txeGetPrivateEvents and never from Noir.
|
|
108
|
+
syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string): Promise<void>;
|
|
86
109
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
3
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
+
import { type NamedValue, Option } from '@aztec/pxe/simulator';
|
|
5
|
+
|
|
6
|
+
import { TXE_ORACLE_REGISTRY } from '../txe_oracle_registry.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fixture definitions for oracle roundtrip tests.
|
|
10
|
+
*
|
|
11
|
+
* Each key is an oracle name from TXE_ORACLE_REGISTRY, and the value is an array of scenarios. The resolver selects a
|
|
12
|
+
* scenario (by name for multi-scenario oracles, or automatically for single-scenario ones), verifies that the
|
|
13
|
+
* deserialized inputs match the fixture's expected values, and returns the fixture output.
|
|
14
|
+
*
|
|
15
|
+
* To add a new oracle test:
|
|
16
|
+
* 1. Add a fixture entry here with the expected inputs and output.
|
|
17
|
+
* 2. Add a Noir `#[oracle_test]` function that calls the oracle with the same input values and asserts the output.
|
|
18
|
+
*/
|
|
19
|
+
export const ORACLE_TEST_FIXTURES: OracleTestFixtures = {
|
|
20
|
+
aztec_avm_storageRead: [
|
|
21
|
+
makeScenario({
|
|
22
|
+
inputs: { slot: new Fr(10), contractAddress: AztecAddress.fromNumber(1) },
|
|
23
|
+
output: new Fr(42),
|
|
24
|
+
}),
|
|
25
|
+
],
|
|
26
|
+
|
|
27
|
+
aztec_prv_getSenderForTags: [
|
|
28
|
+
makeScenario({
|
|
29
|
+
scenario: 'some',
|
|
30
|
+
inputs: {},
|
|
31
|
+
output: Option.some(AztecAddress.fromNumber(42)),
|
|
32
|
+
}),
|
|
33
|
+
makeScenario({
|
|
34
|
+
scenario: 'none',
|
|
35
|
+
inputs: {},
|
|
36
|
+
output: Option.none(AztecAddress.ZERO),
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Converts a `NamedValue` tuple (from `deserializeParams`) to a record.
|
|
43
|
+
* `[NamedValue<'slot', Fr>, NamedValue<'addr', AztecAddress>]` → `{ slot: Fr, addr: AztecAddress }`.
|
|
44
|
+
*/
|
|
45
|
+
type NamedValuesToRecord<T extends readonly NamedValue[]> = {
|
|
46
|
+
[K in T[number] as K extends NamedValue<infer N, any> ? N : never]: K extends NamedValue<string, infer V> ? V : never;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Extracts the deserialized inputs record type from an oracle registry entry. */
|
|
50
|
+
type OracleInputs<K extends keyof typeof TXE_ORACLE_REGISTRY> = (typeof TXE_ORACLE_REGISTRY)[K] extends {
|
|
51
|
+
deserializeParams(...args: any[]): infer P;
|
|
52
|
+
}
|
|
53
|
+
? P extends readonly NamedValue[]
|
|
54
|
+
? NamedValuesToRecord<P>
|
|
55
|
+
: Record<string, never>
|
|
56
|
+
: Record<string, never>;
|
|
57
|
+
|
|
58
|
+
/** Extracts the return type from an oracle registry entry's serializeReturn method. */
|
|
59
|
+
type OracleOutput<K extends keyof typeof TXE_ORACLE_REGISTRY> = (typeof TXE_ORACLE_REGISTRY)[K] extends {
|
|
60
|
+
serializeReturn(result: infer R): any;
|
|
61
|
+
}
|
|
62
|
+
? R
|
|
63
|
+
: void;
|
|
64
|
+
|
|
65
|
+
/** A single test scenario for an oracle, typed against the oracle registry. */
|
|
66
|
+
export interface OracleTestScenario<K extends keyof typeof TXE_ORACLE_REGISTRY = keyof typeof TXE_ORACLE_REGISTRY> {
|
|
67
|
+
/** Scenario name for selection via `#[oracle_test("name")]`. Required when an oracle has multiple scenarios. */
|
|
68
|
+
scenario?: string;
|
|
69
|
+
/** Expected deserialized inputs. */
|
|
70
|
+
inputs: OracleInputs<K>;
|
|
71
|
+
/** Return value to serialize back to Noir. */
|
|
72
|
+
output: OracleOutput<K>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type OracleTestFixtures = {
|
|
76
|
+
[K in keyof typeof TXE_ORACLE_REGISTRY]?: OracleTestScenario<K>[];
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** Creates a typed fixture scenario for a given oracle. */
|
|
80
|
+
function makeScenario<K extends keyof typeof TXE_ORACLE_REGISTRY>(
|
|
81
|
+
scenario: OracleTestScenario<K>,
|
|
82
|
+
): OracleTestScenario<K> {
|
|
83
|
+
return scenario;
|
|
84
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
|
|
3
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
4
|
+
import type { ApiSchemaFor } from '@aztec/foundation/schemas';
|
|
5
|
+
import { zodFor } from '@aztec/stdlib/schemas';
|
|
6
|
+
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
import { ForeignCallArgsSchema, type ForeignCallResult, ForeignCallResultSchema } from '../../utils/encoding.js';
|
|
10
|
+
import { TXE_ORACLE_REGISTRY } from '../txe_oracle_registry.js';
|
|
11
|
+
import { ORACLE_TEST_FIXTURES } from './fixtures.js';
|
|
12
|
+
import { type OracleTestCallInput, OracleTestResolver } from './resolver.js';
|
|
13
|
+
|
|
14
|
+
const OracleTestCallInputSchema = zodFor<OracleTestCallInput>()(
|
|
15
|
+
z.object({
|
|
16
|
+
session_id: z.number().nonnegative(),
|
|
17
|
+
function: z.string(),
|
|
18
|
+
root_path: z.string(),
|
|
19
|
+
package_name: z.string(),
|
|
20
|
+
inputs: ForeignCallArgsSchema,
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
/** Narrowed interface exposing only the RPC method. */
|
|
25
|
+
interface OracleTestRpcHandler {
|
|
26
|
+
resolve_foreign_call(callData: OracleTestCallInput): Promise<ForeignCallResult>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const OracleTestRpcHandlerSchema: ApiSchemaFor<OracleTestRpcHandler> = {
|
|
30
|
+
resolve_foreign_call: z.function({
|
|
31
|
+
input: z.tuple([OracleTestCallInputSchema]),
|
|
32
|
+
output: ForeignCallResultSchema,
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Creates an RPC server backed by the oracle test resolver. */
|
|
37
|
+
export function createOracleTestRpcServer(logger: Logger) {
|
|
38
|
+
const resolver = new OracleTestResolver(TXE_ORACLE_REGISTRY, ORACLE_TEST_FIXTURES, logger);
|
|
39
|
+
const server = createSafeJsonRpcServer<OracleTestRpcHandler>(resolver, OracleTestRpcHandlerSchema, {
|
|
40
|
+
http200OnError: true,
|
|
41
|
+
});
|
|
42
|
+
return { server, resolver };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { OracleTestResolver } from './resolver.js';
|