@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.
- package/dest/oracle/interfaces.d.ts +51 -0
- package/dest/oracle/interfaces.d.ts.map +1 -0
- package/dest/oracle/interfaces.js +3 -0
- package/dest/oracle/txe_oracle_public_context.d.ts +5 -3
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +14 -3
- package/dest/oracle/txe_oracle_top_level_context.d.ts +9 -7
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +24 -24
- package/dest/rpc_translator.d.ts +13 -4
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +101 -66
- package/dest/txe_session.d.ts +15 -15
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +141 -99
- package/dest/utils/tx_effect_creation.d.ts +5 -0
- package/dest/utils/tx_effect_creation.d.ts.map +1 -0
- package/dest/utils/tx_effect_creation.js +16 -0
- package/package.json +15 -15
- package/src/oracle/interfaces.ts +80 -0
- package/src/oracle/txe_oracle_public_context.ts +20 -15
- package/src/oracle/txe_oracle_top_level_context.ts +26 -43
- package/src/rpc_translator.ts +125 -69
- package/src/txe_session.ts +196 -120
- package/src/utils/tx_effect_creation.ts +37 -0
- package/dest/oracle/txe_oracle.d.ts +0 -64
- package/dest/oracle/txe_oracle.d.ts.map +0 -1
- package/dest/oracle/txe_oracle.js +0 -263
- package/dest/oracle/txe_typed_oracle.d.ts +0 -41
- package/dest/oracle/txe_typed_oracle.d.ts.map +0 -1
- package/dest/oracle/txe_typed_oracle.js +0 -89
- package/src/oracle/txe_oracle.ts +0 -419
- package/src/oracle/txe_typed_oracle.ts +0 -147
package/dest/txe_session.js
CHANGED
|
@@ -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 {
|
|
10
|
-
import {
|
|
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 =
|
|
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
|
-
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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 =
|
|
119
|
-
|
|
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
|
|
122
|
-
this.
|
|
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 =
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
|
162
|
-
this.
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
this.
|
|
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
|
-
|
|
171
|
-
this.
|
|
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
|
-
|
|
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
|
|
180
|
-
this.
|
|
181
|
-
|
|
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
|
|
185
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
65
|
-
"@aztec/archiver": "3.0.0-nightly.
|
|
66
|
-
"@aztec/aztec-node": "3.0.0-nightly.
|
|
67
|
-
"@aztec/aztec.js": "3.0.0-nightly.
|
|
68
|
-
"@aztec/bb-prover": "3.0.0-nightly.
|
|
69
|
-
"@aztec/constants": "3.0.0-nightly.
|
|
70
|
-
"@aztec/foundation": "3.0.0-nightly.
|
|
71
|
-
"@aztec/key-store": "3.0.0-nightly.
|
|
72
|
-
"@aztec/kv-store": "3.0.0-nightly.
|
|
73
|
-
"@aztec/protocol-contracts": "3.0.0-nightly.
|
|
74
|
-
"@aztec/pxe": "3.0.0-nightly.
|
|
75
|
-
"@aztec/simulator": "3.0.0-nightly.
|
|
76
|
-
"@aztec/stdlib": "3.0.0-nightly.
|
|
77
|
-
"@aztec/world-state": "3.0.0-nightly.
|
|
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
|
|
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 {
|
|
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
|
-
|
|
43
|
+
avmOpcodeAddress(): Promise<AztecAddress> {
|
|
43
44
|
return Promise.resolve(this.contractAddress);
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
|
|
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
|
-
|
|
55
|
+
avmOpcodeTimestamp(): Promise<bigint> {
|
|
51
56
|
return Promise.resolve(this.globalVariables.timestamp);
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
avmOpcodeIsStaticCall(): Promise<boolean> {
|
|
55
60
|
return Promise.resolve(false);
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
avmOpcodeChainId(): Promise<Fr> {
|
|
59
64
|
return Promise.resolve(this.globalVariables.chainId);
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
|
|
67
|
+
avmOpcodeVersion(): Promise<Fr> {
|
|
63
68
|
return Promise.resolve(this.globalVariables.version);
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|