@aztec/txe 3.0.0-nightly.20250925 → 3.0.0-nightly.20250927

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 (33) hide show
  1. package/dest/oracle/interfaces.d.ts +51 -0
  2. package/dest/oracle/interfaces.d.ts.map +1 -0
  3. package/dest/oracle/interfaces.js +3 -0
  4. package/dest/oracle/txe_oracle_public_context.d.ts +5 -3
  5. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  6. package/dest/oracle/txe_oracle_public_context.js +14 -3
  7. package/dest/oracle/txe_oracle_top_level_context.d.ts +9 -7
  8. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  9. package/dest/oracle/txe_oracle_top_level_context.js +24 -24
  10. package/dest/rpc_translator.d.ts +13 -4
  11. package/dest/rpc_translator.d.ts.map +1 -1
  12. package/dest/rpc_translator.js +101 -66
  13. package/dest/txe_session.d.ts +15 -15
  14. package/dest/txe_session.d.ts.map +1 -1
  15. package/dest/txe_session.js +141 -99
  16. package/dest/utils/tx_effect_creation.d.ts +5 -0
  17. package/dest/utils/tx_effect_creation.d.ts.map +1 -0
  18. package/dest/utils/tx_effect_creation.js +16 -0
  19. package/package.json +15 -15
  20. package/src/oracle/interfaces.ts +80 -0
  21. package/src/oracle/txe_oracle_public_context.ts +20 -15
  22. package/src/oracle/txe_oracle_top_level_context.ts +26 -43
  23. package/src/rpc_translator.ts +125 -69
  24. package/src/txe_session.ts +196 -120
  25. package/src/utils/tx_effect_creation.ts +37 -0
  26. package/dest/oracle/txe_oracle.d.ts +0 -64
  27. package/dest/oracle/txe_oracle.d.ts.map +0 -1
  28. package/dest/oracle/txe_oracle.js +0 -263
  29. package/dest/oracle/txe_typed_oracle.d.ts +0 -41
  30. package/dest/oracle/txe_typed_oracle.d.ts.map +0 -1
  31. package/dest/oracle/txe_typed_oracle.js +0 -89
  32. package/src/oracle/txe_oracle.ts +0 -419
  33. package/src/oracle/txe_typed_oracle.ts +0 -147
@@ -3,42 +3,21 @@ import { createLogger } from '@aztec/foundation/log';
3
3
  import { KeyStore } from '@aztec/key-store';
4
4
  import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
5
5
  import { AddressDataProvider, CapsuleDataProvider, NoteDataProvider, PXEOracleInterface, PrivateEventDataProvider, TaggingDataProvider } from '@aztec/pxe/server';
6
+ import { ExecutionNoteCache, HashedValuesCache, PrivateExecutionOracle, UtilityExecutionOracle } from '@aztec/pxe/simulator';
6
7
  import { FunctionSelector } from '@aztec/stdlib/abi';
7
8
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
9
+ import { Body, L2Block } from '@aztec/stdlib/block';
8
10
  import { GasSettings } from '@aztec/stdlib/gas';
9
- import { PrivateContextInputs } from '@aztec/stdlib/kernel';
10
- import { makeGlobalVariables } from '@aztec/stdlib/testing';
11
- import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
12
- import { TXE } from './oracle/txe_oracle.js';
11
+ import { makeAppendOnlyTreeSnapshot, makeGlobalVariables } from '@aztec/stdlib/testing';
12
+ import { CallContext, TxContext } from '@aztec/stdlib/tx';
13
13
  import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
14
14
  import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js';
15
15
  import { RPCTranslator } from './rpc_translator.js';
16
16
  import { TXEStateMachine } from './state_machine/index.js';
17
17
  import { TXEAccountDataProvider } from './util/txe_account_data_provider.js';
18
18
  import { TXEContractDataProvider } from './util/txe_contract_data_provider.js';
19
- import { getSingleTxBlockRequestHash } from './utils/block_creation.js';
20
- /**
21
- * A TXE Session can be ine one of four states, which change as the test progresses and different oracles are called.
22
- * The current state determines which oracles are available.
23
- */ var SessionState = /*#__PURE__*/ function(SessionState) {
24
- /**
25
- * The top-level state is the default state, before any other state has been entered. This is where contracts can be
26
- * deployed, accounts created, blocks mined, etc.
27
- */ SessionState[SessionState["TOP_LEVEL"] = 0] = "TOP_LEVEL";
28
- /**
29
- * The private state is entered via the `private_context` function. In this state the PXE oracles that `#[private]`
30
- * functions use are available, such as those related to note retrieval, notification of side-effects, capsule access,
31
- * etc. */ SessionState[SessionState["PRIVATE"] = 1] = "PRIVATE";
32
- /**
33
- * The public state is entered via the `public_context` function. In this state the AVM opcodes that `#[public]`
34
- * functions execute are resolved as oracles by TXE, since Noir tests are not transpiled. */ SessionState[SessionState["PUBLIC"] = 2] = "PUBLIC";
35
- /**
36
- * The utility state is entered via the `utility_context` function. In this state the PXE oracles that `#[utility]`
37
- * functions use are available, such as those related to (unconstrained) note retrieval, capsule access, public
38
- * storage reads, etc.
39
- */ SessionState[SessionState["UTILITY"] = 3] = "UTILITY";
40
- return SessionState;
41
- }(SessionState || {});
19
+ import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlockHeader } from './utils/block_creation.js';
20
+ import { makeTxEffect } from './utils/tx_effect_creation.js';
42
21
  const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
43
22
  /**
44
23
  * A `TXESession` corresponds to a Noir `#[test]` function, and handles all of its oracle calls, stores test-specific
@@ -56,6 +35,7 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
56
35
  nextBlockTimestamp;
57
36
  pxeOracleInterface;
58
37
  state;
38
+ authwits;
59
39
  constructor(logger, stateMachine, oracleHandler, contractDataProvider, keyStore, addressDataProvider, accountDataProvider, chainId, version, nextBlockTimestamp, pxeOracleInterface){
60
40
  this.logger = logger;
61
41
  this.stateMachine = stateMachine;
@@ -68,7 +48,10 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
68
48
  this.version = version;
69
49
  this.nextBlockTimestamp = nextBlockTimestamp;
70
50
  this.pxeOracleInterface = pxeOracleInterface;
71
- this.state = 0;
51
+ this.state = {
52
+ name: 'TOP_LEVEL'
53
+ };
54
+ this.authwits = new Map();
72
55
  }
73
56
  static async init(protocolContracts) {
74
57
  const store = await openTmpStore('txe-session');
@@ -90,7 +73,7 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
90
73
  const version = new Fr(await stateMachine.node.getVersion());
91
74
  const chainId = new Fr(await stateMachine.node.getChainId());
92
75
  const pxeOracleInterface = new PXEOracleInterface(stateMachine.node, keyStore, contractDataProvider, noteDataProvider, capsuleDataProvider, stateMachine.syncDataProvider, taggingDataProvider, addressDataProvider, privateEventDataProvider);
93
- const topLevelOracleHandler = new TXEOracleTopLevelContext(stateMachine, contractDataProvider, keyStore, addressDataProvider, accountDataProvider, pxeOracleInterface, nextBlockTimestamp, version, chainId);
76
+ const topLevelOracleHandler = new TXEOracleTopLevelContext(stateMachine, contractDataProvider, keyStore, addressDataProvider, accountDataProvider, pxeOracleInterface, nextBlockTimestamp, version, chainId, new Map());
94
77
  await topLevelOracleHandler.txeAdvanceBlocksBy(1);
95
78
  return new TXESession(createLogger('txe:session'), stateMachine, topLevelOracleHandler, contractDataProvider, keyStore, addressDataProvider, accountDataProvider, version, chainId, nextBlockTimestamp, pxeOracleInterface);
96
79
  }
@@ -100,26 +83,85 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
100
83
  * @param inputs The inputs of the oracle.
101
84
  * @returns The oracle return values.
102
85
  */ processFunction(functionName, inputs) {
103
- return new RPCTranslator(this, this.oracleHandler)[functionName](...inputs);
86
+ try {
87
+ return new RPCTranslator(this, this.oracleHandler)[functionName](...inputs);
88
+ } catch (error) {
89
+ if (error instanceof Error) {
90
+ throw new Error(`Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`);
91
+ } else {
92
+ throw new Error(`Unknown execution error while processing function ${functionName} in state ${this.state.name}`);
93
+ }
94
+ }
104
95
  }
105
- async setTopLevelContext() {
106
- if (this.state == 1) {
107
- await this.exitPrivateContext();
108
- } else if (this.state == 2) {
109
- await this.exitPublicContext();
110
- } else if (this.state == 3) {
111
- this.exitUtilityContext();
112
- } else if (this.state == 0) {
113
- throw new Error(`Expected to be in state other than ${SessionState[0]}`);
114
- } else {
115
- throw new Error(`Unexpected state '${this.state}'`);
96
+ async enterTopLevelState() {
97
+ switch(this.state.name){
98
+ case 'PRIVATE':
99
+ {
100
+ await this.exitPrivateState();
101
+ break;
102
+ }
103
+ case 'PUBLIC':
104
+ {
105
+ await this.exitPublicState();
106
+ break;
107
+ }
108
+ case 'UTILITY':
109
+ {
110
+ this.exitUtilityContext();
111
+ break;
112
+ }
113
+ case 'TOP_LEVEL':
114
+ {
115
+ throw new Error(`Expected to be in state other than TOP_LEVEL`);
116
+ }
117
+ default:
118
+ {
119
+ this.state;
120
+ }
116
121
  }
117
- this.oracleHandler = new TXEOracleTopLevelContext(this.stateMachine, this.contractDataProvider, this.keyStore, this.addressDataProvider, this.accountDataProvider, this.pxeOracleInterface, this.nextBlockTimestamp, this.version, this.chainId);
118
- this.state = 0;
119
- this.logger.debug(`Entered state ${SessionState[this.state]}`);
122
+ this.oracleHandler = new TXEOracleTopLevelContext(this.stateMachine, this.contractDataProvider, this.keyStore, this.addressDataProvider, this.accountDataProvider, this.pxeOracleInterface, this.nextBlockTimestamp, this.version, this.chainId, this.authwits);
123
+ this.state = {
124
+ name: 'TOP_LEVEL'
125
+ };
126
+ this.logger.debug(`Entered state ${this.state.name}`);
127
+ }
128
+ async enterPrivateState(contractAddress = DEFAULT_ADDRESS, anchorBlockNumber) {
129
+ this.exitTopLevelState();
130
+ // There is no automatic message discovery and contract-driven syncing process in inlined private or utility
131
+ // contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
132
+ // we perform this. We therefore search for known nullifiers now, as otherwise notes that were nullified would not
133
+ // be removed from the database.
134
+ // TODO(#12553): make the synchronizer sync here instead and remove this
135
+ await this.pxeOracleInterface.removeNullifiedNotes(contractAddress);
136
+ // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
137
+ // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
138
+ // a single transaction with the effects of what was done in the test.
139
+ const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
140
+ const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
141
+ const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
142
+ blockNumber: latestBlock.globalVariables.blockNumber + 1,
143
+ timestamp: this.nextBlockTimestamp,
144
+ version: this.version,
145
+ chainId: this.chainId
146
+ });
147
+ const txRequestHash = getSingleTxBlockRequestHash(nextBlockGlobalVariables.blockNumber);
148
+ const noteCache = new ExecutionNoteCache(txRequestHash);
149
+ this.oracleHandler = new PrivateExecutionOracle(Fr.ZERO, new TxContext(this.chainId, this.version, GasSettings.empty()), new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false), anchorBlock, [], [], new HashedValuesCache(), noteCache, this.pxeOracleInterface);
150
+ // We store the note cache fed into the PrivateExecutionOracle (along with some other auxiliary data) in order to
151
+ // refer to it later, mimicking the way this object is used by the ContractFunctionSimulator. The difference resides
152
+ // in that the simulator has all information needed in order to run the simulation, while ours will be ongoing as
153
+ // the different oracles will be invoked from the Noir test, until eventually the private execution finishes.
154
+ this.state = {
155
+ name: 'PRIVATE',
156
+ nextBlockGlobalVariables,
157
+ txRequestHash,
158
+ noteCache
159
+ };
160
+ this.logger.debug(`Entered state ${this.state.name}`);
161
+ return this.oracleHandler.getPrivateContextInputs();
120
162
  }
121
- async setPublicContext(contractAddress) {
122
- this.exitTopLevelContext();
163
+ async enterPublicState(contractAddress) {
164
+ this.exitTopLevelState();
123
165
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
124
166
  // the test. The block therefore gets the *next* block number and timestamp.
125
167
  const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest')).globalVariables.blockNumber;
@@ -130,73 +172,73 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
130
172
  chainId: this.chainId
131
173
  });
132
174
  this.oracleHandler = new TXEOraclePublicContext(contractAddress ?? DEFAULT_ADDRESS, await this.stateMachine.synchronizer.nativeWorldStateService.fork(), getSingleTxBlockRequestHash(globalVariables.blockNumber), globalVariables);
133
- this.state = 2;
134
- this.logger.debug(`Entered state ${SessionState[this.state]}`);
135
- }
136
- async setPrivateContext(contractAddress, anchorBlockNumber) {
137
- this.exitTopLevelContext();
138
- // A PrivateContext has two associated block numbers: the anchor block (i.e. the historical block that is used to build the
139
- // proof), and the *next* block, i.e. the one the PrivateContext will create with the single transaction that
140
- // contains the effects of what was done in the test.
141
- const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
142
- const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
143
- const anchorBlockGlobalVariables = makeGlobalVariables(undefined, {
144
- blockNumber: anchorBlock.globalVariables.blockNumber,
145
- timestamp: anchorBlock.globalVariables.timestamp,
146
- version: this.version,
147
- chainId: this.chainId
148
- });
149
- const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
150
- blockNumber: latestBlock.globalVariables.blockNumber + 1,
151
- timestamp: this.nextBlockTimestamp,
152
- version: this.version,
153
- chainId: this.chainId
154
- });
155
- const privateContextInputs = await this.getPrivateContextInputs(anchorBlockGlobalVariables.blockNumber, contractAddress ?? DEFAULT_ADDRESS);
156
- this.oracleHandler = await TXE.create(contractAddress ?? DEFAULT_ADDRESS, this.pxeOracleInterface, await this.stateMachine.synchronizer.nativeWorldStateService.fork(), anchorBlockGlobalVariables, nextBlockGlobalVariables, getSingleTxBlockRequestHash(nextBlockGlobalVariables.blockNumber));
157
- this.state = 1;
158
- this.logger.debug(`Entered state ${SessionState[this.state]}`);
159
- return privateContextInputs;
175
+ this.state = {
176
+ name: 'PUBLIC'
177
+ };
178
+ this.logger.debug(`Entered state ${this.state.name}`);
160
179
  }
161
- async setUtilityContext(contractAddress) {
162
- this.exitTopLevelContext();
163
- // A UtilityContext is built using the latest block as a reference, mimicking what would happen if PXE had synced
164
- // all the way to the tip of the chain.
165
- const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
166
- this.oracleHandler = await TXE.create(contractAddress ?? DEFAULT_ADDRESS, this.pxeOracleInterface, await this.stateMachine.synchronizer.nativeWorldStateService.fork(), latestBlock.globalVariables, GlobalVariables.empty(), Fr.random());
167
- this.state = 3;
168
- this.logger.debug(`Entered state ${SessionState[this.state]}`);
180
+ async enterUtilityState(contractAddress = DEFAULT_ADDRESS) {
181
+ this.exitTopLevelState();
182
+ // There is no automatic message discovery and contract-driven syncing process in inlined private or utility
183
+ // contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
184
+ // we perform this. We therefore search for known nullifiers now, as otherwise notes that were nullified would not
185
+ // be removed from the database.
186
+ // TODO(#12553): make the synchronizer sync here instead and remove this
187
+ await this.pxeOracleInterface.removeNullifiedNotes(contractAddress);
188
+ this.oracleHandler = new UtilityExecutionOracle(contractAddress, [], [], this.pxeOracleInterface);
189
+ this.state = {
190
+ name: 'UTILITY'
191
+ };
192
+ this.logger.debug(`Entered state ${this.state.name}`);
169
193
  }
170
- exitTopLevelContext() {
171
- this.assertState(0);
194
+ exitTopLevelState() {
195
+ if (this.state.name != 'TOP_LEVEL') {
196
+ throw new Error(`Expected to be in state 'TOP_LEVEL', but got '${this.state.name}' instead`);
197
+ }
172
198
  // Note that while all public and private contexts do is build a single block that we then process when exiting
173
199
  // those, the top level context performs a large number of actions not captured in the following 'close' call. Among
174
200
  // others, it will create empty blocks (via `txeAdvanceBlocksBy` and `deploy`), create blocks with transactions via
175
201
  // `txePrivateCallNewFlow` and `txePublicCallNewFlow`, add accounts to PXE via `txeAddAccount`, etc. This is a
176
202
  // slight inconsistency in the working model of this class, but is not too bad.
177
- this.nextBlockTimestamp = this.oracleHandler.close();
203
+ // TODO: it's quite unfortunate that we need to capture the authwits created to later pass them again when the top
204
+ // level context is re-created. This is because authwits create a temporary utility context that'd otherwise reset
205
+ // the authwits if not persisted, so we'd not be able to pass more than one per execution.
206
+ // Ideally authwits would be passed alongside a contract call instead of pre-seeded.
207
+ [this.nextBlockTimestamp, this.authwits] = this.oracleHandler.close();
178
208
  }
179
- async exitPublicContext() {
180
- this.assertState(2);
181
- const block = await this.oracleHandler.close();
209
+ async exitPrivateState() {
210
+ if (this.state.name != 'PRIVATE') {
211
+ throw new Error(`Expected to be in state 'PRIVATE', but got '${this.state.name}' instead`);
212
+ }
213
+ this.logger.debug('Exiting Private state, building block with collected side effects', {
214
+ blockNumber: this.state.nextBlockGlobalVariables.blockNumber
215
+ });
216
+ // We rely on the note cache to determine the effects of the transaction. This is incomplete as it doesn't private
217
+ // logs (other effects like enqueued public calls don't need to be considered since those are not allowed).
218
+ const txEffect = await makeTxEffect(this.state.noteCache, this.state.txRequestHash, this.state.nextBlockGlobalVariables.blockNumber);
219
+ // We build a block holding just this transaction
220
+ const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
221
+ await insertTxEffectIntoWorldTrees(txEffect, forkedWorldTrees);
222
+ const block = new L2Block(makeAppendOnlyTreeSnapshot(), await makeTXEBlockHeader(forkedWorldTrees, this.state.nextBlockGlobalVariables), new Body([
223
+ txEffect
224
+ ]));
182
225
  await this.stateMachine.handleL2Block(block);
226
+ await forkedWorldTrees.close();
227
+ this.logger.debug('Exited PublicContext with built block', {
228
+ blockNumber: block.number,
229
+ txEffects: block.body.txEffects
230
+ });
183
231
  }
184
- async exitPrivateContext() {
185
- this.assertState(1);
232
+ async exitPublicState() {
233
+ if (this.state.name != 'PUBLIC') {
234
+ throw new Error(`Expected to be in state 'PUBLIC', but got '${this.state.name}' instead`);
235
+ }
186
236
  const block = await this.oracleHandler.close();
187
237
  await this.stateMachine.handleL2Block(block);
188
238
  }
189
239
  exitUtilityContext() {
190
- this.assertState(3);
191
- }
192
- assertState(state) {
193
- if (this.state != state) {
194
- throw new Error(`Expected to be in state ${SessionState[state]}, but got '${SessionState[this.state]}' instead`);
240
+ if (this.state.name != 'UTILITY') {
241
+ throw new Error(`Expected to be in state 'UTILITY', but got '${this.state.name}' instead`);
195
242
  }
196
243
  }
197
- async getPrivateContextInputs(anchorBlockNumber, contractAddress) {
198
- this.logger.info(`Creating private context for block ${anchorBlockNumber}`);
199
- const sender = await AztecAddress.random();
200
- return new PrivateContextInputs(new CallContext(sender, contractAddress, FunctionSelector.empty(), false), await this.stateMachine.node.getBlockHeader(anchorBlockNumber), new TxContext(this.chainId, this.version, GasSettings.empty()), 0);
201
- }
202
244
  }
@@ -0,0 +1,5 @@
1
+ import { Fr } from '@aztec/foundation/fields';
2
+ import type { ExecutionNoteCache } from '@aztec/pxe/simulator';
3
+ import { TxEffect } from '@aztec/stdlib/tx';
4
+ export declare function makeTxEffect(noteCache: ExecutionNoteCache, txRequestHash: Fr, txBlockNumber: number): Promise<TxEffect>;
5
+ //# sourceMappingURL=tx_effect_creation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tx_effect_creation.d.ts","sourceRoot":"","sources":["../../src/utils/tx_effect_creation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,QAAQ,EAAU,MAAM,kBAAkB,CAAC;AAEpD,wBAAsB,YAAY,CAChC,SAAS,EAAE,kBAAkB,EAC7B,aAAa,EAAE,EAAE,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,QAAQ,CAAC,CA2BnB"}
@@ -0,0 +1,16 @@
1
+ import { Fr } from '@aztec/foundation/fields';
2
+ import { computeNoteHashNonce, computeUniqueNoteHash, siloNoteHash } from '@aztec/stdlib/hash';
3
+ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
4
+ export async function makeTxEffect(noteCache, txRequestHash, txBlockNumber) {
5
+ const txEffect = TxEffect.empty();
6
+ const { usedTxRequestHashForNonces } = noteCache.finish();
7
+ const nonceGenerator = usedTxRequestHashForNonces ? txRequestHash : noteCache.getAllNullifiers()[0];
8
+ txEffect.noteHashes = await Promise.all(noteCache.getAllNotes().map(async (pendingNote, i)=>computeUniqueNoteHash(await computeNoteHashNonce(nonceGenerator, i), await siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption))));
9
+ // Nullifiers are already siloed
10
+ txEffect.nullifiers = noteCache.getAllNullifiers();
11
+ if (usedTxRequestHashForNonces) {
12
+ txEffect.nullifiers.unshift(txRequestHash);
13
+ }
14
+ txEffect.txHash = new TxHash(new Fr(txBlockNumber));
15
+ return txEffect;
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/txe",
3
- "version": "3.0.0-nightly.20250925",
3
+ "version": "3.0.0-nightly.20250927",
4
4
  "type": "module",
5
5
  "exports": "./dest/index.js",
6
6
  "bin": "./dest/bin/index.js",
@@ -61,20 +61,20 @@
61
61
  ]
62
62
  },
63
63
  "dependencies": {
64
- "@aztec/accounts": "3.0.0-nightly.20250925",
65
- "@aztec/archiver": "3.0.0-nightly.20250925",
66
- "@aztec/aztec-node": "3.0.0-nightly.20250925",
67
- "@aztec/aztec.js": "3.0.0-nightly.20250925",
68
- "@aztec/bb-prover": "3.0.0-nightly.20250925",
69
- "@aztec/constants": "3.0.0-nightly.20250925",
70
- "@aztec/foundation": "3.0.0-nightly.20250925",
71
- "@aztec/key-store": "3.0.0-nightly.20250925",
72
- "@aztec/kv-store": "3.0.0-nightly.20250925",
73
- "@aztec/protocol-contracts": "3.0.0-nightly.20250925",
74
- "@aztec/pxe": "3.0.0-nightly.20250925",
75
- "@aztec/simulator": "3.0.0-nightly.20250925",
76
- "@aztec/stdlib": "3.0.0-nightly.20250925",
77
- "@aztec/world-state": "3.0.0-nightly.20250925",
64
+ "@aztec/accounts": "3.0.0-nightly.20250927",
65
+ "@aztec/archiver": "3.0.0-nightly.20250927",
66
+ "@aztec/aztec-node": "3.0.0-nightly.20250927",
67
+ "@aztec/aztec.js": "3.0.0-nightly.20250927",
68
+ "@aztec/bb-prover": "3.0.0-nightly.20250927",
69
+ "@aztec/constants": "3.0.0-nightly.20250927",
70
+ "@aztec/foundation": "3.0.0-nightly.20250927",
71
+ "@aztec/key-store": "3.0.0-nightly.20250927",
72
+ "@aztec/kv-store": "3.0.0-nightly.20250927",
73
+ "@aztec/protocol-contracts": "3.0.0-nightly.20250927",
74
+ "@aztec/pxe": "3.0.0-nightly.20250927",
75
+ "@aztec/simulator": "3.0.0-nightly.20250927",
76
+ "@aztec/stdlib": "3.0.0-nightly.20250927",
77
+ "@aztec/world-state": "3.0.0-nightly.20250927",
78
78
  "zod": "^3.23.8"
79
79
  },
80
80
  "devDependencies": {
@@ -0,0 +1,80 @@
1
+ import type { CompleteAddress, ContractArtifact, ContractInstanceWithAddress, TxHash } from '@aztec/aztec.js';
2
+ import type { Fr } from '@aztec/foundation/fields';
3
+ import type { FunctionSelector } from '@aztec/stdlib/abi';
4
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
5
+ import type { UInt32, UInt64 } from '@aztec/stdlib/types';
6
+
7
+ // These interfaces complement the ones defined in PXE, and combined with those contain the full list of oracles used by
8
+ // aztec-nr. In particular, these include the ones needed to run Brillig code associated to #[public] functions that has
9
+ // not been transpiled (e.g. in the context of a Noir test) as well as the ones associated with managing the state of
10
+ // such a Noir test (deploying contracts, manipulating block time, making calls, etc) - the so called 'top level test
11
+ // context'.
12
+
13
+ /**
14
+ * Oracle methods associated with the execution of an Aztec #[public] function.
15
+ *
16
+ * Note that real contracts have their Brillig calls to these be transpiled into opcodes, the oracles are only executed
17
+ * as such when running the original Brillig code, e.g. when invoking functions that interact with a PublicContext
18
+ * directly in a Noir test.
19
+ */
20
+ export interface IAvmExecutionOracle {
21
+ isAvm: true;
22
+
23
+ avmOpcodeAddress(): Promise<AztecAddress>;
24
+ avmOpcodeSender(): Promise<AztecAddress>;
25
+ avmOpcodeBlockNumber(): Promise<UInt32>;
26
+ avmOpcodeTimestamp(): Promise<bigint>;
27
+ avmOpcodeIsStaticCall(): Promise<boolean>;
28
+ avmOpcodeChainId(): Promise<Fr>;
29
+ avmOpcodeVersion(): Promise<Fr>;
30
+ avmOpcodeEmitNullifier(nullifier: Fr): Promise<void>;
31
+ avmOpcodeEmitNoteHash(noteHash: Fr): Promise<void>;
32
+ avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean>;
33
+ avmOpcodeStorageWrite(slot: Fr, value: Fr): Promise<void>;
34
+ avmOpcodeStorageRead(slot: Fr): Promise<Fr>;
35
+ }
36
+
37
+ /**
38
+ * Oracle methods associated with the execution of an Aztec Noir test.
39
+ */
40
+ export interface ITxeExecutionOracle {
41
+ isTxe: true;
42
+
43
+ txeGetNextBlockNumber(): Promise<number>;
44
+ txeGetNextBlockTimestamp(): Promise<UInt64>;
45
+ txeAdvanceBlocksBy(blocks: number): Promise<void>;
46
+ txeAdvanceTimestampBy(duration: UInt64): void;
47
+ txeDeploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, foreignSecret: Fr): Promise<void>;
48
+ txeCreateAccount(secret: Fr): Promise<CompleteAddress>;
49
+ txeAddAccount(
50
+ artifact: ContractArtifact,
51
+ instance: ContractInstanceWithAddress,
52
+ secret: Fr,
53
+ ): Promise<CompleteAddress>;
54
+ txeAddAuthWitness(address: AztecAddress, messageHash: Fr): Promise<void>;
55
+ txeGetLastBlockTimestamp(): Promise<bigint>;
56
+ txeGetLastTxEffects(): Promise<{
57
+ txHash: TxHash;
58
+ noteHashes: Fr[];
59
+ nullifiers: Fr[];
60
+ }>;
61
+ txePrivateCallNewFlow(
62
+ from: AztecAddress,
63
+ targetContractAddress: AztecAddress,
64
+ functionSelector: FunctionSelector,
65
+ args: Fr[],
66
+ argsHash: Fr,
67
+ isStaticCall: boolean,
68
+ ): Promise<Fr[]>;
69
+ txeSimulateUtilityFunction(
70
+ targetContractAddress: AztecAddress,
71
+ functionSelector: FunctionSelector,
72
+ args: Fr[],
73
+ ): Promise<Fr[]>;
74
+ txePublicCallNewFlow(
75
+ from: AztecAddress,
76
+ targetContractAddress: AztecAddress,
77
+ calldata: Fr[],
78
+ isStaticCall: boolean,
79
+ ): Promise<Fr[]>;
80
+ }
@@ -1,7 +1,7 @@
1
1
  import { Fr } from '@aztec/foundation/fields';
2
2
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
3
  import { PublicDataWrite } from '@aztec/stdlib/avm';
4
- import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
+ import { AztecAddress } from '@aztec/stdlib/aztec-address';
5
5
  import { Body, L2Block } from '@aztec/stdlib/block';
6
6
  import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/stdlib/hash';
7
7
  import { makeAppendOnlyTreeSnapshot } from '@aztec/stdlib/testing';
@@ -15,9 +15,11 @@ import { GlobalVariables, TxEffect, TxHash } from '@aztec/stdlib/tx';
15
15
  import type { UInt32 } from '@aztec/stdlib/types';
16
16
 
17
17
  import { insertTxEffectIntoWorldTrees, makeTXEBlockHeader } from '../utils/block_creation.js';
18
- import { TXETypedOracle } from './txe_typed_oracle.js';
18
+ import type { IAvmExecutionOracle } from './interfaces.js';
19
+
20
+ export class TXEOraclePublicContext implements IAvmExecutionOracle {
21
+ isAvm = true as const;
19
22
 
20
- export class TXEOraclePublicContext extends TXETypedOracle {
21
23
  private logger: Logger;
22
24
  private transientUniqueNoteHashes: Fr[] = [];
23
25
  private transientSiloedNullifiers: Fr[] = [];
@@ -29,7 +31,6 @@ export class TXEOraclePublicContext extends TXETypedOracle {
29
31
  private txRequestHash: Fr,
30
32
  private globalVariables: GlobalVariables,
31
33
  ) {
32
- super('TXEOraclePublicContext');
33
34
  this.logger = createLogger('txe:public_context');
34
35
 
35
36
  this.logger.debug('Entering Public Context', {
@@ -39,42 +40,46 @@ export class TXEOraclePublicContext extends TXETypedOracle {
39
40
  });
40
41
  }
41
42
 
42
- override avmOpcodeAddress(): Promise<AztecAddress> {
43
+ avmOpcodeAddress(): Promise<AztecAddress> {
43
44
  return Promise.resolve(this.contractAddress);
44
45
  }
45
46
 
46
- override avmOpcodeBlockNumber(): Promise<UInt32> {
47
+ avmOpcodeSender(): Promise<AztecAddress> {
48
+ return Promise.resolve(AztecAddress.ZERO); // todo: change?
49
+ }
50
+
51
+ avmOpcodeBlockNumber(): Promise<UInt32> {
47
52
  return Promise.resolve(this.globalVariables.blockNumber);
48
53
  }
49
54
 
50
- override avmOpcodeTimestamp(): Promise<bigint> {
55
+ avmOpcodeTimestamp(): Promise<bigint> {
51
56
  return Promise.resolve(this.globalVariables.timestamp);
52
57
  }
53
58
 
54
- override avmOpcodeIsStaticCall(): Promise<boolean> {
59
+ avmOpcodeIsStaticCall(): Promise<boolean> {
55
60
  return Promise.resolve(false);
56
61
  }
57
62
 
58
- override avmOpcodeChainId(): Promise<Fr> {
63
+ avmOpcodeChainId(): Promise<Fr> {
59
64
  return Promise.resolve(this.globalVariables.chainId);
60
65
  }
61
66
 
62
- override avmOpcodeVersion(): Promise<Fr> {
67
+ avmOpcodeVersion(): Promise<Fr> {
63
68
  return Promise.resolve(this.globalVariables.version);
64
69
  }
65
70
 
66
- override async avmOpcodeEmitNullifier(nullifier: Fr) {
71
+ async avmOpcodeEmitNullifier(nullifier: Fr) {
67
72
  const siloedNullifier = await siloNullifier(this.contractAddress, nullifier);
68
73
  this.transientSiloedNullifiers.push(siloedNullifier);
69
74
  }
70
75
 
71
- override async avmOpcodeEmitNoteHash(noteHash: Fr) {
76
+ async avmOpcodeEmitNoteHash(noteHash: Fr) {
72
77
  const siloedNoteHash = await siloNoteHash(this.contractAddress, noteHash);
73
78
  // TODO: make the note hash unique - they are only siloed right now
74
79
  this.transientUniqueNoteHashes.push(siloedNoteHash);
75
80
  }
76
81
 
77
- override async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean> {
82
+ async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean> {
78
83
  const nullifier = await siloNullifier(targetAddress, innerNullifier!);
79
84
 
80
85
  const treeIndex = (
@@ -85,7 +90,7 @@ export class TXEOraclePublicContext extends TXETypedOracle {
85
90
  return treeIndex !== undefined || transientIndex !== undefined;
86
91
  }
87
92
 
88
- override async avmOpcodeStorageWrite(slot: Fr, value: Fr) {
93
+ async avmOpcodeStorageWrite(slot: Fr, value: Fr) {
89
94
  this.logger.debug('AVM storage write', { slot, value });
90
95
 
91
96
  const dataWrite = new PublicDataWrite(await computePublicDataTreeLeafSlot(this.contractAddress, slot), value);
@@ -97,7 +102,7 @@ export class TXEOraclePublicContext extends TXETypedOracle {
97
102
  ]);
98
103
  }
99
104
 
100
- override async avmOpcodeStorageRead(slot: Fr): Promise<Fr> {
105
+ async avmOpcodeStorageRead(slot: Fr): Promise<Fr> {
101
106
  const leafSlot = await computePublicDataTreeLeafSlot(this.contractAddress, slot);
102
107
 
103
108
  const lowLeafResult = await this.forkedWorldTrees.getPreviousValueIndex(