@aztec/txe 0.0.0-test.1 → 0.0.1-commit.017a351

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 (182) hide show
  1. package/dest/ContractClassRegistry-PII7Q556.js +3 -0
  2. package/dest/ContractClassRegistry-PII7Q556.js.map +7 -0
  3. package/dest/ContractInstanceRegistry-3MHLGFNZ.js +3 -0
  4. package/dest/ContractInstanceRegistry-3MHLGFNZ.js.map +7 -0
  5. package/dest/FeeJuice-TQV5AQK7.js +3 -0
  6. package/dest/FeeJuice-TQV5AQK7.js.map +7 -0
  7. package/dest/MultiCallEntrypoint-SBJUIJY7.js +3 -0
  8. package/dest/MultiCallEntrypoint-SBJUIJY7.js.map +7 -0
  9. package/dest/SchnorrAccount-MQXPIZ44.js +3 -0
  10. package/dest/SchnorrAccount-MQXPIZ44.js.map +7 -0
  11. package/dest/bin/check_txe_oracle_version.d.ts +2 -0
  12. package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
  13. package/dest/bin/check_txe_oracle_version.js +61 -0
  14. package/dest/bin/index.d.ts +1 -1
  15. package/dest/bin/index.js +3 -29
  16. package/dest/bin/index.js.map +7 -0
  17. package/dest/bin/oracle_test_server.d.ts +3 -0
  18. package/dest/bin/oracle_test_server.d.ts.map +1 -0
  19. package/dest/bin/oracle_test_server.js +41 -0
  20. package/dest/chunk-BA54BEZH.js +265 -0
  21. package/dest/chunk-BA54BEZH.js.map +7 -0
  22. package/dest/chunk-BJVAAXNA.js +3 -0
  23. package/dest/chunk-BJVAAXNA.js.map +7 -0
  24. package/dest/chunk-ZKW35DT6.js +304 -0
  25. package/dest/chunk-ZKW35DT6.js.map +7 -0
  26. package/dest/constants.d.ts +7 -0
  27. package/dest/constants.d.ts.map +1 -0
  28. package/dest/constants.js +10 -0
  29. package/dest/dispatcher_pool.d.ts +67 -0
  30. package/dest/dispatcher_pool.d.ts.map +1 -0
  31. package/dest/dispatcher_pool.js +284 -0
  32. package/dest/index.d.ts +185 -7
  33. package/dest/index.d.ts.map +1 -1
  34. package/dest/index.js +76 -132
  35. package/dest/metafile.json +38691 -0
  36. package/dest/msgpackr_fr_extension.d.ts +2 -0
  37. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  38. package/dest/msgpackr_fr_extension.js +21 -0
  39. package/dest/oracle/interfaces.d.ts +83 -0
  40. package/dest/oracle/interfaces.d.ts.map +1 -0
  41. package/dest/oracle/interfaces.js +3 -0
  42. package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
  43. package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
  44. package/dest/oracle/test-resolver/fixtures.js +39 -0
  45. package/dest/oracle/test-resolver/index.d.ts +9 -0
  46. package/dest/oracle/test-resolver/index.d.ts.map +1 -0
  47. package/dest/oracle/test-resolver/index.js +33 -0
  48. package/dest/oracle/test-resolver/resolver.d.ts +34 -0
  49. package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
  50. package/dest/oracle/test-resolver/resolver.js +114 -0
  51. package/dest/oracle/txe_oracle_public_context.d.ts +58 -0
  52. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -0
  53. package/dest/oracle/txe_oracle_public_context.js +164 -0
  54. package/dest/oracle/txe_oracle_registry.d.ts +147 -0
  55. package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
  56. package/dest/oracle/txe_oracle_registry.js +562 -0
  57. package/dest/oracle/txe_oracle_top_level_context.d.ts +85 -0
  58. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -0
  59. package/dest/oracle/txe_oracle_top_level_context.js +634 -0
  60. package/dest/oracle/txe_oracle_version.d.ts +17 -0
  61. package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
  62. package/dest/oracle/txe_oracle_version.js +14 -0
  63. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  64. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  65. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  66. package/dest/rpc_server.d.ts +14 -0
  67. package/dest/rpc_server.d.ts.map +1 -0
  68. package/dest/rpc_server.js +78 -0
  69. package/dest/rpc_translator.d.ts +120 -0
  70. package/dest/rpc_translator.d.ts.map +1 -0
  71. package/dest/rpc_translator.js +882 -0
  72. package/dest/server.bundle.js +3 -0
  73. package/dest/server.bundle.js.map +7 -0
  74. package/dest/state_machine/archiver.d.ts +32 -0
  75. package/dest/state_machine/archiver.d.ts.map +1 -0
  76. package/dest/state_machine/archiver.js +96 -0
  77. package/dest/state_machine/dummy_p2p_client.d.ts +65 -0
  78. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -0
  79. package/dest/state_machine/dummy_p2p_client.js +165 -0
  80. package/dest/state_machine/global_variable_builder.d.ts +16 -0
  81. package/dest/state_machine/global_variable_builder.d.ts.map +1 -0
  82. package/dest/state_machine/global_variable_builder.js +28 -0
  83. package/dest/state_machine/index.d.ts +20 -0
  84. package/dest/state_machine/index.d.ts.map +1 -0
  85. package/dest/state_machine/index.js +86 -0
  86. package/dest/state_machine/mock_epoch_cache.d.ts +42 -0
  87. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -0
  88. package/dest/state_machine/mock_epoch_cache.js +85 -0
  89. package/dest/state_machine/synchronizer.d.ts +33 -0
  90. package/dest/state_machine/synchronizer.d.ts.map +1 -0
  91. package/dest/state_machine/synchronizer.js +59 -0
  92. package/dest/txe_session.d.ts +140 -0
  93. package/dest/txe_session.d.ts.map +1 -0
  94. package/dest/txe_session.js +499 -0
  95. package/dest/utils/block_creation.d.ts +28 -0
  96. package/dest/utils/block_creation.d.ts.map +1 -0
  97. package/dest/utils/block_creation.js +58 -0
  98. package/dest/utils/encoding.d.ts +191 -0
  99. package/dest/utils/encoding.d.ts.map +1 -0
  100. package/dest/utils/encoding.js +136 -0
  101. package/dest/utils/expected_failure_error.d.ts +4 -0
  102. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  103. package/dest/utils/tx_effect_creation.d.ts +5 -0
  104. package/dest/utils/tx_effect_creation.d.ts.map +1 -0
  105. package/dest/utils/tx_effect_creation.js +13 -0
  106. package/dest/utils/txe_account_store.d.ts +10 -0
  107. package/dest/utils/txe_account_store.d.ts.map +1 -0
  108. package/dest/{util/txe_account_data_provider.js → utils/txe_account_store.js} +1 -1
  109. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  110. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  111. package/dest/utils/txe_artifact_resolver.js +161 -0
  112. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  113. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  114. package/dest/utils/txe_public_contract_data_source.js +48 -0
  115. package/dest/worker.bundle.js +3 -0
  116. package/dest/worker.bundle.js.map +7 -0
  117. package/dest/worker.d.ts +2 -0
  118. package/dest/worker.d.ts.map +1 -0
  119. package/dest/worker.js +92 -0
  120. package/package.json +52 -29
  121. package/src/bin/check_txe_oracle_version.ts +70 -0
  122. package/src/bin/index.ts +14 -4
  123. package/src/bin/oracle_test_server.ts +51 -0
  124. package/src/constants.ts +13 -0
  125. package/src/dispatcher_pool.ts +316 -0
  126. package/src/index.ts +113 -175
  127. package/src/msgpackr_fr_extension.ts +23 -0
  128. package/src/oracle/interfaces.ts +109 -0
  129. package/src/oracle/test-resolver/fixtures.ts +84 -0
  130. package/src/oracle/test-resolver/index.ts +45 -0
  131. package/src/oracle/test-resolver/resolver.ts +165 -0
  132. package/src/oracle/txe_oracle_public_context.ts +218 -0
  133. package/src/oracle/txe_oracle_registry.ts +401 -0
  134. package/src/oracle/txe_oracle_top_level_context.ts +898 -0
  135. package/src/oracle/txe_oracle_version.ts +17 -0
  136. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  137. package/src/rpc_server.ts +87 -0
  138. package/src/rpc_translator.ts +1103 -0
  139. package/src/state_machine/archiver.ts +119 -0
  140. package/src/state_machine/dummy_p2p_client.ts +247 -0
  141. package/src/state_machine/global_variable_builder.ts +53 -0
  142. package/src/state_machine/index.ts +135 -0
  143. package/src/state_machine/mock_epoch_cache.ts +107 -0
  144. package/src/state_machine/synchronizer.ts +88 -0
  145. package/src/txe_session.ts +799 -0
  146. package/src/utils/block_creation.ts +97 -0
  147. package/src/utils/encoding.ts +172 -0
  148. package/src/utils/tx_effect_creation.ts +30 -0
  149. package/src/{util/txe_account_data_provider.ts → utils/txe_account_store.ts} +1 -1
  150. package/src/utils/txe_artifact_resolver.ts +217 -0
  151. package/src/utils/txe_public_contract_data_source.ts +58 -0
  152. package/src/worker.ts +98 -0
  153. package/dest/node/txe_node.d.ts +0 -358
  154. package/dest/node/txe_node.d.ts.map +0 -1
  155. package/dest/node/txe_node.js +0 -504
  156. package/dest/oracle/txe_oracle.d.ts +0 -152
  157. package/dest/oracle/txe_oracle.d.ts.map +0 -1
  158. package/dest/oracle/txe_oracle.js +0 -833
  159. package/dest/txe_service/txe_service.d.ts +0 -212
  160. package/dest/txe_service/txe_service.d.ts.map +0 -1
  161. package/dest/txe_service/txe_service.js +0 -572
  162. package/dest/util/encoding.d.ts +0 -103
  163. package/dest/util/encoding.d.ts.map +0 -1
  164. package/dest/util/encoding.js +0 -76
  165. package/dest/util/expected_failure_error.d.ts +0 -4
  166. package/dest/util/expected_failure_error.d.ts.map +0 -1
  167. package/dest/util/txe_account_data_provider.d.ts +0 -10
  168. package/dest/util/txe_account_data_provider.d.ts.map +0 -1
  169. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  170. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  171. package/dest/util/txe_public_contract_data_source.js +0 -88
  172. package/dest/util/txe_world_state_db.d.ts +0 -14
  173. package/dest/util/txe_world_state_db.d.ts.map +0 -1
  174. package/dest/util/txe_world_state_db.js +0 -27
  175. package/src/node/txe_node.ts +0 -725
  176. package/src/oracle/txe_oracle.ts +0 -1241
  177. package/src/txe_service/txe_service.ts +0 -749
  178. package/src/util/encoding.ts +0 -92
  179. package/src/util/txe_public_contract_data_source.ts +0 -101
  180. package/src/util/txe_world_state_db.ts +0 -38
  181. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  182. /package/src/{util → utils}/expected_failure_error.ts +0 -0
@@ -0,0 +1,109 @@
1
+ import { CompleteAddress } from '@aztec/aztec.js/addresses';
2
+ import { TxHash } from '@aztec/aztec.js/tx';
3
+ import { BlockNumber } from '@aztec/foundation/branded-types';
4
+ import type { Fr } from '@aztec/foundation/curves/bn254';
5
+ import type { EventSelector, FunctionSelector } from '@aztec/stdlib/abi';
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
+ import type { UInt64 } from '@aztec/stdlib/types';
10
+
11
+ // These interfaces complement the ones defined in PXE, and combined with those contain the full list of oracles used by
12
+ // aztec-nr. In particular, these include the ones needed to run Brillig code associated to #[external("public")] functions that has
13
+ // not been transpiled (e.g. in the context of a Noir test) as well as the ones associated with managing the state of
14
+ // such a Noir test (deploying contracts, manipulating block time, making calls, etc) - the so called 'top level test
15
+ // context'.
16
+
17
+ /**
18
+ * Oracle methods associated with the execution of an Aztec #[external("public")] function.
19
+ *
20
+ * Note that real contracts have their Brillig calls to these be transpiled into opcodes, the oracles are only executed
21
+ * as such when running the original Brillig code, e.g. when invoking functions that interact with a PublicContext
22
+ * directly in a Noir test.
23
+ */
24
+ export interface IAvmExecutionOracle {
25
+ isAvm: true;
26
+
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>;
48
+ }
49
+
50
+ /**
51
+ * Oracle methods associated with the execution of an Aztec Noir test.
52
+ */
53
+ export interface ITxeExecutionOracle {
54
+ isTxe: true;
55
+
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[],
65
+ secret: Fr,
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<{
74
+ txHash: TxHash;
75
+ noteHashes: Fr[];
76
+ nullifiers: Fr[];
77
+ privateLogs: PrivateLog[];
78
+ }>;
79
+ getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress): Promise<Fr[][]>;
80
+ privateCallNewFlow(
81
+ from: AztecAddress | undefined,
82
+ targetContractAddress: AztecAddress,
83
+ functionSelector: FunctionSelector,
84
+ args: Fr[],
85
+ argsHash: Fr,
86
+ isStaticCall: boolean,
87
+ additionalScopes: AztecAddress[],
88
+ jobId: string,
89
+ authorizedUtilityCallTargets: AztecAddress[],
90
+ gasSettings: GasSettings,
91
+ ): Promise<{ returnValues: Fr[]; offchainEffects: Fr[][] }>;
92
+ executeUtilityFunction(
93
+ targetContractAddress: AztecAddress,
94
+ functionSelector: FunctionSelector,
95
+ args: Fr[],
96
+ jobId: string,
97
+ authorizedUtilityCallTargets: AztecAddress[],
98
+ ): Promise<Fr[]>;
99
+ publicCallNewFlow(
100
+ from: AztecAddress | undefined,
101
+ targetContractAddress: AztecAddress,
102
+ calldata: Fr[],
103
+ isStaticCall: boolean,
104
+ gasSettings: GasSettings,
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>;
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';
@@ -0,0 +1,165 @@
1
+ /* eslint-disable camelcase */
2
+ import type { Logger } from '@aztec/foundation/log';
3
+ import { createLogger } from '@aztec/foundation/log';
4
+ import { withHexPrefix, withoutHexPrefix } from '@aztec/foundation/string';
5
+ import type { InputSlot, OracleRegistryEntry } from '@aztec/pxe/simulator';
6
+
7
+ import type { ForeignCallArgs, ForeignCallResult } from '../../utils/encoding.js';
8
+ import type { OracleTestScenario } from './fixtures.js';
9
+
10
+ /** Name of the meta-oracle that Noir tests call to select a fixture scenario by name. */
11
+ const SET_SCENARIO_ORACLE = 'aztec_oracle_test_set_scenario';
12
+
13
+ export type OracleTestCallInput = {
14
+ session_id: number;
15
+ function: string;
16
+ root_path: string;
17
+ package_name: string;
18
+ inputs: ForeignCallArgs;
19
+ };
20
+
21
+ /**
22
+ * Oracle resolver for roundtrip tests. Selects a fixture scenario, verifies that the received inputs match, and
23
+ * returns the corresponding fixture output.
24
+ *
25
+ * For oracles with a single fixture, it is selected automatically. For oracles with multiple fixtures, Noir tests
26
+ * must call the `oracle_test_set_scenario` meta-oracle to select a specific scenario by name before calling the
27
+ * real oracle.
28
+ */
29
+ export class OracleTestResolver {
30
+ private readonly calledOracles = new Set<string>();
31
+ private readonly pendingScenario = new Map<number, string>();
32
+ private readonly logger: Logger;
33
+
34
+ constructor(
35
+ private readonly registry: Record<string, OracleRegistryEntry>,
36
+ private readonly fixtures: Record<string, OracleTestScenario[]>,
37
+ logger?: Logger,
38
+ ) {
39
+ this.logger = logger ?? createLogger('txe:test-resolver');
40
+ }
41
+
42
+ // eslint-disable-next-line require-await
43
+ async resolve_foreign_call(callData: OracleTestCallInput): Promise<ForeignCallResult> {
44
+ const oracleName = callData.function;
45
+ this.logger.debug('Resolving oracle', { oracleName });
46
+
47
+ if (oracleName === SET_SCENARIO_ORACLE) {
48
+ return this.#handleSetScenario(callData);
49
+ }
50
+
51
+ this.calledOracles.add(oracleName);
52
+
53
+ if (!(oracleName in this.registry)) {
54
+ throw new Error(`Oracle '${oracleName}' not found in registry`);
55
+ }
56
+ const entry = this.registry[oracleName];
57
+
58
+ const scenarios = this.fixtures[oracleName];
59
+ if (!scenarios || scenarios.length === 0) {
60
+ throw new Error(`No fixture defined for oracle '${oracleName}'`);
61
+ }
62
+
63
+ const match = this.#selectScenario(callData.session_id, oracleName, scenarios);
64
+ this.#verifyInputs(callData.inputs, entry, match, oracleName);
65
+
66
+ this.logger.debug('Verified scenario for oracle', { oracleName });
67
+
68
+ const outputSlots = entry.serializeReturn(match.output);
69
+ return {
70
+ values: outputSlots.map(slot => (Array.isArray(slot) ? slot.map(withoutHexPrefix) : withoutHexPrefix(slot))),
71
+ };
72
+ }
73
+
74
+ /** Returns oracles that have a fixture defined but were never called during testing. */
75
+ getUncalledFixtures(): string[] {
76
+ return Object.keys(this.fixtures).filter(name => !this.calledOracles.has(name));
77
+ }
78
+
79
+ /** Returns oracles in the registry that have no fixture defined at all. */
80
+ getMissingFixtures(): string[] {
81
+ return Object.keys(this.registry).filter(name => !(name in this.fixtures));
82
+ }
83
+
84
+ #handleSetScenario(callData: { session_id: number; inputs: ForeignCallArgs }): ForeignCallResult {
85
+ const charCodes = callData.inputs[0] as string[];
86
+ const scenarioName = charCodes
87
+ .map(hex => Number(BigInt(hex.startsWith('0x') ? hex : `0x${hex}`)))
88
+ .map(code => String.fromCharCode(code))
89
+ .join('');
90
+
91
+ this.logger.debug('Setting scenario for next oracle call', { sessionId: callData.session_id, scenarioName });
92
+ this.pendingScenario.set(callData.session_id, scenarioName);
93
+
94
+ return { values: [] };
95
+ }
96
+
97
+ /** Selects a scenario by pending name (for multi-fixture oracles) or returns the single fixture. */
98
+ #selectScenario(sessionId: number, oracleName: string, fixtures: OracleTestScenario[]): OracleTestScenario {
99
+ const selectedName = this.pendingScenario.get(sessionId);
100
+
101
+ if (selectedName !== undefined) {
102
+ this.pendingScenario.delete(sessionId);
103
+ const matches = fixtures.filter(s => s.scenario === selectedName);
104
+ if (matches.length === 0) {
105
+ const available = fixtures.map(s => s.scenario).join(', ');
106
+ throw new Error(`No scenario named '${selectedName}' for oracle '${oracleName}'. Available: ${available}`);
107
+ }
108
+ if (matches.length > 1) {
109
+ throw new Error(`Duplicate scenario name '${selectedName}' for oracle '${oracleName}'`);
110
+ }
111
+ return matches[0];
112
+ }
113
+
114
+ if (fixtures.length === 1) {
115
+ return fixtures[0];
116
+ }
117
+
118
+ throw new Error(
119
+ `Oracle '${oracleName}' has ${fixtures.length} fixture scenarios. ` +
120
+ `Use #[oracle_test("scenario_name")] to select one.`,
121
+ );
122
+ }
123
+
124
+ /** Deserializes actual inputs and verifies they match the scenario's expected inputs. */
125
+ #verifyInputs(
126
+ rawInputs: ForeignCallArgs,
127
+ entry: OracleRegistryEntry,
128
+ scenario: OracleTestScenario,
129
+ oracleName: string,
130
+ ): void {
131
+ const expectedInputs = scenario.inputs as Record<string, unknown>;
132
+ if (Object.keys(expectedInputs).length === 0) {
133
+ return;
134
+ }
135
+
136
+ const normalized: InputSlot[] = rawInputs.map(v =>
137
+ Array.isArray(v) ? (v as string[]).map(withHexPrefix) : [withHexPrefix(v as string)],
138
+ );
139
+ const named = entry.deserializeParams(normalized);
140
+
141
+ for (const param of named) {
142
+ if (!(param.name in expectedInputs)) {
143
+ throw new Error(
144
+ `Unexpected param '${param.name}' for oracle '${oracleName}'. ` +
145
+ `Expected: ${Object.keys(expectedInputs).join(', ')}`,
146
+ );
147
+ }
148
+ if (!valuesEqual(param.value, expectedInputs[param.name])) {
149
+ const scenarioLabel = scenario.scenario ? ` (scenario '${scenario.scenario}')` : '';
150
+ throw new Error(
151
+ `Input mismatch for oracle '${oracleName}'${scenarioLabel}: ` +
152
+ `param '${param.name}' expected ${String(expectedInputs[param.name])} ` +
153
+ `but got ${String(param.value)}`,
154
+ );
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ function valuesEqual(actual: unknown, expected: unknown): boolean {
161
+ if (Array.isArray(actual) && Array.isArray(expected)) {
162
+ return actual.length === expected.length && actual.every((v, i) => valuesEqual(v, expected[i]));
163
+ }
164
+ return String(actual) === String(expected);
165
+ }
@@ -0,0 +1,218 @@
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
+ import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { type Logger, createLogger } from '@aztec/foundation/log';
4
+ import type { ContractStore } from '@aztec/pxe/server';
5
+ import { PublicDataWrite } from '@aztec/stdlib/avm';
6
+ import { AztecAddress } from '@aztec/stdlib/aztec-address';
7
+ import type { L2Block } from '@aztec/stdlib/block';
8
+ import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
9
+ import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/stdlib/hash';
10
+ import {
11
+ MerkleTreeId,
12
+ type MerkleTreeWriteOperations,
13
+ PublicDataTreeLeaf,
14
+ PublicDataTreeLeafPreimage,
15
+ } from '@aztec/stdlib/trees';
16
+ import { GlobalVariables, TxEffect, TxHash } from '@aztec/stdlib/tx';
17
+
18
+ import { insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
19
+ import type { IAvmExecutionOracle } from './interfaces.js';
20
+
21
+ export class TXEOraclePublicContext implements IAvmExecutionOracle {
22
+ isAvm = true as const;
23
+
24
+ private logger: Logger;
25
+ private transientUniqueNoteHashes: Fr[] = [];
26
+ private transientSiloedNullifiers: Fr[] = [];
27
+ private publicDataWrites: PublicDataWrite[] = [];
28
+
29
+ constructor(
30
+ private contractAddress: AztecAddress,
31
+ private forkedWorldTrees: MerkleTreeWriteOperations,
32
+ private txRequestHash: Fr,
33
+ private globalVariables: GlobalVariables,
34
+ private contractStore: ContractStore,
35
+ ) {
36
+ this.logger = createLogger('txe:public_context');
37
+
38
+ this.logger.debug('Entering Public Context', {
39
+ contractAddress,
40
+ blockNumber: globalVariables.blockNumber,
41
+ timestamp: globalVariables.timestamp,
42
+ });
43
+ }
44
+
45
+ address(): Promise<AztecAddress> {
46
+ return Promise.resolve(this.contractAddress);
47
+ }
48
+
49
+ sender(): Promise<AztecAddress> {
50
+ return Promise.resolve(AztecAddress.ZERO); // todo: change?
51
+ }
52
+
53
+ blockNumber(): Promise<BlockNumber> {
54
+ return Promise.resolve(this.globalVariables.blockNumber);
55
+ }
56
+
57
+ timestamp(): Promise<bigint> {
58
+ return Promise.resolve(this.globalVariables.timestamp);
59
+ }
60
+
61
+ isStaticCall(): Promise<boolean> {
62
+ return Promise.resolve(false);
63
+ }
64
+
65
+ chainId(): Promise<Fr> {
66
+ return Promise.resolve(this.globalVariables.chainId);
67
+ }
68
+
69
+ version(): Promise<Fr> {
70
+ return Promise.resolve(this.globalVariables.version);
71
+ }
72
+
73
+ async emitNullifier(nullifier: Fr) {
74
+ const siloedNullifier = await siloNullifier(this.contractAddress, nullifier);
75
+ this.transientSiloedNullifiers.push(siloedNullifier);
76
+ }
77
+
78
+ async emitNoteHash(noteHash: Fr) {
79
+ const siloedNoteHash = await siloNoteHash(this.contractAddress, noteHash);
80
+ // TODO: make the note hash unique - they are only siloed right now
81
+ this.transientUniqueNoteHashes.push(siloedNoteHash);
82
+ }
83
+
84
+ async nullifierExists(siloedNullifier: Fr): Promise<boolean> {
85
+ const treeIndex = (
86
+ await this.forkedWorldTrees.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()])
87
+ )[0];
88
+ const transientIndex = this.transientSiloedNullifiers.find(n => n.equals(siloedNullifier));
89
+
90
+ return treeIndex !== undefined || transientIndex !== undefined;
91
+ }
92
+
93
+ async storageWrite(slot: Fr, value: Fr) {
94
+ this.logger.debug('AVM storage write', { slot, value });
95
+
96
+ const dataWrite = new PublicDataWrite(await computePublicDataTreeLeafSlot(this.contractAddress, slot), value);
97
+
98
+ this.publicDataWrites.push(dataWrite);
99
+
100
+ await this.forkedWorldTrees.sequentialInsert(MerkleTreeId.PUBLIC_DATA_TREE, [
101
+ new PublicDataTreeLeaf(dataWrite.leafSlot, dataWrite.value).toBuffer(),
102
+ ]);
103
+ }
104
+
105
+ async storageRead(slot: Fr, contractAddress: AztecAddress): Promise<Fr> {
106
+ const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot);
107
+
108
+ const lowLeafResult = await this.forkedWorldTrees.getPreviousValueIndex(
109
+ MerkleTreeId.PUBLIC_DATA_TREE,
110
+ leafSlot.toBigInt(),
111
+ );
112
+
113
+ const value =
114
+ !lowLeafResult || !lowLeafResult.alreadyPresent
115
+ ? Fr.ZERO
116
+ : (
117
+ (await this.forkedWorldTrees.getLeafPreimage(
118
+ MerkleTreeId.PUBLIC_DATA_TREE,
119
+ lowLeafResult.index,
120
+ )) as PublicDataTreeLeafPreimage
121
+ ).leaf.value;
122
+
123
+ this.logger.debug('AVM storage read', { slot, contractAddress, value });
124
+
125
+ return value;
126
+ }
127
+
128
+ getContractInstanceDeployer(address: AztecAddress): Promise<{ member: Fr; exists: boolean }> {
129
+ return this.getContractInstanceMember(address, i => i.deployer.toField());
130
+ }
131
+
132
+ getContractInstanceClassId(address: AztecAddress): Promise<{ member: Fr; exists: boolean }> {
133
+ return this.getContractInstanceMember(address, i => i.currentContractClassId);
134
+ }
135
+
136
+ getContractInstanceInitializationHash(address: AztecAddress): Promise<{ member: Fr; exists: boolean }> {
137
+ return this.getContractInstanceMember(address, i => i.initializationHash);
138
+ }
139
+
140
+ getContractInstanceImmutablesHash(address: AztecAddress): Promise<{ member: Fr; exists: boolean }> {
141
+ return this.getContractInstanceMember(address, i => i.immutablesHash);
142
+ }
143
+
144
+ private async getContractInstanceMember(
145
+ address: AztecAddress,
146
+ accessor: (instance: ContractInstanceWithAddress) => Fr,
147
+ ): Promise<{ member: Fr; exists: boolean }> {
148
+ const instance = await this.contractStore.getContractInstance(address);
149
+ if (!instance) {
150
+ return { member: Fr.ZERO, exists: false };
151
+ }
152
+ return { member: accessor(instance), exists: true };
153
+ }
154
+
155
+ returndataSize(): Promise<Fr> {
156
+ throw new Error(
157
+ 'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
158
+ );
159
+ }
160
+
161
+ returndataCopy(_rdOffset: number, _copySize: number): Promise<Fr[]> {
162
+ throw new Error(
163
+ 'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
164
+ );
165
+ }
166
+
167
+ call(_l2Gas: number, _daGas: number, _address: AztecAddress, _argsLength: number, _args: Fr[]): Promise<Fr[]> {
168
+ throw new Error(
169
+ 'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
170
+ );
171
+ }
172
+
173
+ staticCall(_l2Gas: number, _daGas: number, _address: AztecAddress, _argsLength: number, _args: Fr[]): Promise<Fr[]> {
174
+ throw new Error(
175
+ 'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
176
+ );
177
+ }
178
+
179
+ successCopy(): Promise<Fr> {
180
+ throw new Error(
181
+ 'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
182
+ );
183
+ }
184
+
185
+ async close(): Promise<L2Block> {
186
+ this.logger.debug('Exiting Public Context, building block with collected side effects', {
187
+ blockNumber: this.globalVariables.blockNumber,
188
+ });
189
+
190
+ const txEffect = this.makeTxEffect();
191
+ await insertTxEffectIntoWorldTrees(txEffect, this.forkedWorldTrees);
192
+
193
+ const block = await makeTXEBlock(this.forkedWorldTrees, this.globalVariables, [txEffect]);
194
+
195
+ await this.forkedWorldTrees.close();
196
+
197
+ this.logger.debug('Exited PublicContext with built block', {
198
+ blockNumber: block.number,
199
+ txEffects: block.body.txEffects,
200
+ });
201
+
202
+ return block;
203
+ }
204
+
205
+ private makeTxEffect(): TxEffect {
206
+ const txEffect = TxEffect.empty();
207
+
208
+ txEffect.noteHashes = this.transientUniqueNoteHashes;
209
+ txEffect.nullifiers = [this.txRequestHash, ...this.transientSiloedNullifiers];
210
+
211
+ txEffect.publicDataWrites = this.publicDataWrites;
212
+ // TODO: support public logs
213
+
214
+ txEffect.txHash = new TxHash(new Fr(this.globalVariables.blockNumber));
215
+
216
+ return txEffect;
217
+ }
218
+ }