@aztec/txe 0.0.1-commit.5daedc8 → 0.0.1-commit.6201a7b05
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/constants.d.ts +3 -0
- package/dest/constants.d.ts.map +1 -0
- package/dest/constants.js +2 -0
- package/dest/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +91 -56
- package/dest/oracle/interfaces.d.ts +36 -29
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.d.ts +16 -16
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +20 -22
- package/dest/oracle/txe_oracle_top_level_context.d.ts +44 -27
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +245 -109
- package/dest/rpc_translator.d.ts +136 -78
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +545 -178
- package/dest/state_machine/archiver.d.ts +21 -52
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +67 -99
- package/dest/state_machine/dummy_p2p_client.d.ts +21 -15
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +45 -25
- package/dest/state_machine/global_variable_builder.d.ts +11 -5
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
- package/dest/state_machine/global_variable_builder.js +21 -3
- package/dest/state_machine/index.d.ts +9 -7
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +45 -24
- package/dest/state_machine/mock_epoch_cache.d.ts +26 -8
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +49 -9
- package/dest/state_machine/synchronizer.d.ts +6 -5
- package/dest/state_machine/synchronizer.d.ts.map +1 -1
- package/dest/state_machine/synchronizer.js +8 -7
- package/dest/txe_session.d.ts +71 -15
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +228 -57
- package/dest/util/encoding.d.ts +688 -19
- package/dest/util/encoding.d.ts.map +1 -1
- package/dest/util/encoding.js +5 -1
- package/dest/util/txe_account_store.d.ts +10 -0
- package/dest/util/txe_account_store.d.ts.map +1 -0
- package/dest/util/{txe_account_data_provider.js → txe_account_store.js} +1 -1
- package/dest/util/txe_public_contract_data_source.d.ts +8 -8
- package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
- package/dest/util/txe_public_contract_data_source.js +13 -32
- package/dest/utils/block_creation.d.ts +21 -6
- package/dest/utils/block_creation.d.ts.map +1 -1
- package/dest/utils/block_creation.js +38 -4
- package/dest/utils/tx_effect_creation.d.ts +3 -3
- package/dest/utils/tx_effect_creation.d.ts.map +1 -1
- package/dest/utils/tx_effect_creation.js +4 -7
- package/package.json +18 -18
- package/src/constants.ts +3 -0
- package/src/index.ts +103 -63
- package/src/oracle/interfaces.ts +37 -33
- package/src/oracle/txe_oracle_public_context.ts +21 -28
- package/src/oracle/txe_oracle_top_level_context.ts +291 -151
- package/src/rpc_translator.ts +645 -206
- package/src/state_machine/archiver.ts +63 -117
- package/src/state_machine/dummy_p2p_client.ts +62 -33
- package/src/state_machine/global_variable_builder.ts +38 -6
- package/src/state_machine/index.ts +68 -22
- package/src/state_machine/mock_epoch_cache.ts +61 -14
- package/src/state_machine/synchronizer.ts +9 -8
- package/src/txe_session.ts +351 -105
- package/src/util/encoding.ts +6 -1
- package/src/util/{txe_account_data_provider.ts → txe_account_store.ts} +1 -1
- package/src/util/txe_public_contract_data_source.ts +20 -47
- package/src/utils/block_creation.ts +49 -15
- package/src/utils/tx_effect_creation.ts +5 -12
- package/dest/util/txe_account_data_provider.d.ts +0 -10
- package/dest/util/txe_account_data_provider.d.ts.map +0 -1
- package/dest/util/txe_contract_data_provider.d.ts +0 -12
- package/dest/util/txe_contract_data_provider.d.ts.map +0 -1
- package/dest/util/txe_contract_data_provider.js +0 -22
- package/src/util/txe_contract_data_provider.ts +0 -36
package/src/txe_session.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
4
|
import { KeyStore } from '@aztec/key-store';
|
|
4
5
|
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
|
|
5
|
-
import type { ProtocolContract } from '@aztec/protocol-contracts';
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
AddressStore,
|
|
8
|
+
AnchorBlockStore,
|
|
9
|
+
CapsuleService,
|
|
10
|
+
CapsuleStore,
|
|
11
|
+
ContractStore,
|
|
12
|
+
JobCoordinator,
|
|
13
|
+
NoteService,
|
|
14
|
+
NoteStore,
|
|
15
|
+
PrivateEventStore,
|
|
16
|
+
RecipientTaggingStore,
|
|
17
|
+
SenderAddressBookStore,
|
|
18
|
+
SenderTaggingStore,
|
|
13
19
|
} from '@aztec/pxe/server';
|
|
14
20
|
import {
|
|
15
21
|
ExecutionNoteCache,
|
|
@@ -17,35 +23,39 @@ import {
|
|
|
17
23
|
HashedValuesCache,
|
|
18
24
|
type IPrivateExecutionOracle,
|
|
19
25
|
type IUtilityExecutionOracle,
|
|
26
|
+
Oracle,
|
|
20
27
|
PrivateExecutionOracle,
|
|
21
28
|
UtilityExecutionOracle,
|
|
22
29
|
} from '@aztec/pxe/simulator';
|
|
23
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
ExecutionError,
|
|
32
|
+
WASMSimulator,
|
|
33
|
+
createSimulationError,
|
|
34
|
+
extractCallStack,
|
|
35
|
+
resolveAssertionMessageFromError,
|
|
36
|
+
toACVMWitness,
|
|
37
|
+
} from '@aztec/simulator/client';
|
|
38
|
+
import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
|
|
24
39
|
import type { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
25
40
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
26
|
-
import { Body, L2Block } from '@aztec/stdlib/block';
|
|
27
41
|
import { GasSettings } from '@aztec/stdlib/gas';
|
|
28
42
|
import { computeProtocolNullifier } from '@aztec/stdlib/hash';
|
|
29
43
|
import { PrivateContextInputs } from '@aztec/stdlib/kernel';
|
|
30
|
-
import {
|
|
31
|
-
import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
|
|
32
|
-
import type { UInt32 } from '@aztec/stdlib/types';
|
|
44
|
+
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
45
|
+
import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
|
|
33
46
|
|
|
34
47
|
import { z } from 'zod';
|
|
35
48
|
|
|
49
|
+
import { DEFAULT_ADDRESS } from './constants.js';
|
|
36
50
|
import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
|
|
37
51
|
import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
|
|
38
52
|
import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js';
|
|
39
53
|
import { RPCTranslator } from './rpc_translator.js';
|
|
54
|
+
import { TXEArchiver } from './state_machine/archiver.js';
|
|
40
55
|
import { TXEStateMachine } from './state_machine/index.js';
|
|
41
56
|
import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
getSingleTxBlockRequestHash,
|
|
46
|
-
insertTxEffectIntoWorldTrees,
|
|
47
|
-
makeTXEBlockHeader,
|
|
48
|
-
} from './utils/block_creation.js';
|
|
57
|
+
import { TXEAccountStore } from './util/txe_account_store.js';
|
|
58
|
+
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
|
|
49
59
|
import { makeTxEffect } from './utils/tx_effect_creation.js';
|
|
50
60
|
|
|
51
61
|
/**
|
|
@@ -67,7 +77,6 @@ type SessionState =
|
|
|
67
77
|
| {
|
|
68
78
|
name: 'PRIVATE';
|
|
69
79
|
nextBlockGlobalVariables: GlobalVariables;
|
|
70
|
-
protocolNullifier: Fr;
|
|
71
80
|
noteCache: ExecutionNoteCache;
|
|
72
81
|
taggingIndexCache: ExecutionTaggingIndexCache;
|
|
73
82
|
}
|
|
@@ -102,11 +111,72 @@ export type TXEOracleFunctionName = Exclude<
|
|
|
102
111
|
export interface TXESessionStateHandler {
|
|
103
112
|
enterTopLevelState(): Promise<void>;
|
|
104
113
|
enterPublicState(contractAddress?: AztecAddress): Promise<void>;
|
|
105
|
-
enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?:
|
|
114
|
+
enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
|
|
106
115
|
enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
|
|
116
|
+
|
|
117
|
+
// TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
|
|
118
|
+
cycleJob(): Promise<string>;
|
|
119
|
+
getCurrentJob(): string;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Runs an executor-style top-level call (private/public call, utility execution) with last-call tracking.
|
|
123
|
+
*/
|
|
124
|
+
withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Captures a raw offchain effect payload for consumption from test environment. Called by the `emit_offchain_effect`
|
|
128
|
+
* oracle handler whenever a contract function emits an offchain message, at any call depth.
|
|
129
|
+
*/
|
|
130
|
+
recordOffchainEffect(data: Fr[]): void;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Returns the raw offchain effect payloads emitted by the last top-level call. Each payload follows the protocol
|
|
134
|
+
* convention documented on `OFFCHAIN_MESSAGE_IDENTIFIER`, i.e. `[identifier, recipient, ...ciphertext]`. Decoding into
|
|
135
|
+
* `OffchainMessage` structs happens on the Noir side of the test helper. Marks the buffer as queried so the
|
|
136
|
+
* unqueried-messages warning doesn't fire on the next reset.
|
|
137
|
+
*/
|
|
138
|
+
getLastCallOffchainEffects(): { effects: Fr[][] };
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Returns the context of the last top-level call: its tx hash (`Fr.ZERO` if the call was tx-less) and the anchor
|
|
142
|
+
* block timestamp captured at the start of the call. Does *not* mark the buffer as queried — context reads are
|
|
143
|
+
* metadata, not effect consumption.
|
|
144
|
+
*/
|
|
145
|
+
getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint };
|
|
107
146
|
}
|
|
108
147
|
|
|
109
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Session state tracking the most recently completed top-level call: the offchain effect buffer it produced, and the
|
|
150
|
+
* call's context (tx hash + anchor block timestamp). The context is refreshed on every top-level call, independently
|
|
151
|
+
* of whether the call produced offchain effects.
|
|
152
|
+
*/
|
|
153
|
+
interface LastCallState {
|
|
154
|
+
/**
|
|
155
|
+
* Raw offchain effect payloads emitted by the currently-executing (or most recently completed) top-level call. Wiped
|
|
156
|
+
* at the start of every top-level entry point, appended to on every `emit_offchain_effect` oracle invocation.
|
|
157
|
+
*/
|
|
158
|
+
offchainEffects: Fr[][];
|
|
159
|
+
/**
|
|
160
|
+
* Tracks whether the test has queried `effects` since the last reset. If a new top-level call clobbers the buffer
|
|
161
|
+
* without it being queried first, any accumulated messages are lost and we emit a warning so tests don't silently
|
|
162
|
+
* drop delivery.
|
|
163
|
+
*/
|
|
164
|
+
queried: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Tx hash of the most recently completed top-level call, or `Fr.ZERO` if the call was tx-less (context setters,
|
|
167
|
+
* utility execution). Populated by call executor handlers after execution completes.
|
|
168
|
+
*/
|
|
169
|
+
txHash: Fr;
|
|
170
|
+
/**
|
|
171
|
+
* Anchor block timestamp of the most recently completed top-level call, captured from the anchor block header that
|
|
172
|
+
* was active when the call started. Populated by call executor handlers after execution completes.
|
|
173
|
+
*/
|
|
174
|
+
anchorBlockTimestamp: bigint;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function emptyLastCallState(): LastCallState {
|
|
178
|
+
return { offchainEffects: [], queried: false, txHash: Fr.ZERO, anchorBlockTimestamp: 0n };
|
|
179
|
+
}
|
|
110
180
|
|
|
111
181
|
/**
|
|
112
182
|
* A `TXESession` corresponds to a Noir `#[test]` function, and handles all of its oracle calls, stores test-specific
|
|
@@ -115,6 +185,7 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
|
|
|
115
185
|
export class TXESession implements TXESessionStateHandler {
|
|
116
186
|
private state: SessionState = { name: 'TOP_LEVEL' };
|
|
117
187
|
private authwits: Map<string, AuthWitness> = new Map();
|
|
188
|
+
private lastCallInfo: LastCallState = emptyLastCallState();
|
|
118
189
|
|
|
119
190
|
constructor(
|
|
120
191
|
private logger: Logger,
|
|
@@ -124,78 +195,96 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
124
195
|
| IPrivateExecutionOracle
|
|
125
196
|
| IAvmExecutionOracle
|
|
126
197
|
| ITxeExecutionOracle,
|
|
127
|
-
private
|
|
198
|
+
private contractStore: ContractStore,
|
|
199
|
+
private noteStore: NoteStore,
|
|
128
200
|
private keyStore: KeyStore,
|
|
129
|
-
private
|
|
130
|
-
private
|
|
201
|
+
private addressStore: AddressStore,
|
|
202
|
+
private accountStore: TXEAccountStore,
|
|
203
|
+
private senderTaggingStore: SenderTaggingStore,
|
|
204
|
+
private recipientTaggingStore: RecipientTaggingStore,
|
|
205
|
+
private senderAddressBookStore: SenderAddressBookStore,
|
|
206
|
+
private capsuleStore: CapsuleStore,
|
|
207
|
+
private privateEventStore: PrivateEventStore,
|
|
208
|
+
private jobCoordinator: JobCoordinator,
|
|
209
|
+
private currentJobId: string,
|
|
131
210
|
private chainId: Fr,
|
|
132
211
|
private version: Fr,
|
|
133
212
|
private nextBlockTimestamp: bigint,
|
|
134
|
-
private pxeOracleInterface: PXEOracleInterface,
|
|
135
213
|
) {}
|
|
136
214
|
|
|
137
|
-
static async init(
|
|
215
|
+
static async init(contractStore: ContractStore) {
|
|
138
216
|
const store = await openTmpStore('txe-session');
|
|
139
217
|
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const
|
|
218
|
+
const addressStore = new AddressStore(store);
|
|
219
|
+
const privateEventStore = new PrivateEventStore(store);
|
|
220
|
+
const noteStore = new NoteStore(store);
|
|
221
|
+
const senderTaggingStore = new SenderTaggingStore(store);
|
|
222
|
+
const recipientTaggingStore = new RecipientTaggingStore(store);
|
|
223
|
+
const senderAddressBookStore = new SenderAddressBookStore(store);
|
|
224
|
+
const capsuleStore = new CapsuleStore(store);
|
|
146
225
|
const keyStore = new KeyStore(store);
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
226
|
+
const accountStore = new TXEAccountStore(store);
|
|
227
|
+
|
|
228
|
+
// Create job coordinator and register staged stores
|
|
229
|
+
const jobCoordinator = new JobCoordinator(store);
|
|
230
|
+
jobCoordinator.registerStores([
|
|
231
|
+
capsuleStore,
|
|
232
|
+
senderTaggingStore,
|
|
233
|
+
recipientTaggingStore,
|
|
234
|
+
privateEventStore,
|
|
235
|
+
noteStore,
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
const archiver = new TXEArchiver(store);
|
|
239
|
+
const anchorBlockStore = new AnchorBlockStore(store);
|
|
240
|
+
const stateMachine = await TXEStateMachine.create(archiver, anchorBlockStore, contractStore, noteStore);
|
|
156
241
|
|
|
157
242
|
const nextBlockTimestamp = BigInt(Math.floor(new Date().getTime() / 1000));
|
|
158
243
|
const version = new Fr(await stateMachine.node.getVersion());
|
|
159
244
|
const chainId = new Fr(await stateMachine.node.getChainId());
|
|
160
245
|
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
contractDataProvider,
|
|
165
|
-
noteDataProvider,
|
|
166
|
-
capsuleDataProvider,
|
|
167
|
-
stateMachine.syncDataProvider,
|
|
168
|
-
taggingDataProvider,
|
|
169
|
-
addressDataProvider,
|
|
170
|
-
privateEventDataProvider,
|
|
171
|
-
);
|
|
246
|
+
const initialJobId = jobCoordinator.beginJob();
|
|
247
|
+
|
|
248
|
+
const logger = createLogger('txe:session');
|
|
172
249
|
|
|
173
250
|
const topLevelOracleHandler = new TXEOracleTopLevelContext(
|
|
174
251
|
stateMachine,
|
|
175
|
-
|
|
252
|
+
contractStore,
|
|
253
|
+
noteStore,
|
|
176
254
|
keyStore,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
255
|
+
addressStore,
|
|
256
|
+
accountStore,
|
|
257
|
+
senderTaggingStore,
|
|
258
|
+
recipientTaggingStore,
|
|
259
|
+
senderAddressBookStore,
|
|
260
|
+
capsuleStore,
|
|
261
|
+
privateEventStore,
|
|
180
262
|
nextBlockTimestamp,
|
|
181
263
|
version,
|
|
182
264
|
chainId,
|
|
183
265
|
new Map(),
|
|
184
266
|
);
|
|
185
|
-
await topLevelOracleHandler.
|
|
267
|
+
await topLevelOracleHandler.advanceBlocksBy(1);
|
|
186
268
|
|
|
187
269
|
return new TXESession(
|
|
188
|
-
|
|
270
|
+
logger,
|
|
189
271
|
stateMachine,
|
|
190
272
|
topLevelOracleHandler,
|
|
191
|
-
|
|
273
|
+
contractStore,
|
|
274
|
+
noteStore,
|
|
192
275
|
keyStore,
|
|
193
|
-
|
|
194
|
-
|
|
276
|
+
addressStore,
|
|
277
|
+
accountStore,
|
|
278
|
+
senderTaggingStore,
|
|
279
|
+
recipientTaggingStore,
|
|
280
|
+
senderAddressBookStore,
|
|
281
|
+
capsuleStore,
|
|
282
|
+
privateEventStore,
|
|
283
|
+
jobCoordinator,
|
|
284
|
+
initialJobId,
|
|
195
285
|
version,
|
|
196
286
|
chainId,
|
|
197
287
|
nextBlockTimestamp,
|
|
198
|
-
pxeOracleInterface,
|
|
199
288
|
);
|
|
200
289
|
}
|
|
201
290
|
|
|
@@ -230,6 +319,61 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
230
319
|
}
|
|
231
320
|
}
|
|
232
321
|
|
|
322
|
+
getCurrentJob(): string {
|
|
323
|
+
return this.currentJobId;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Commits the current job and begins a new one. Returns the new job ID. */
|
|
327
|
+
async cycleJob(): Promise<string> {
|
|
328
|
+
await this.jobCoordinator.commitJob(this.currentJobId);
|
|
329
|
+
this.currentJobId = this.jobCoordinator.beginJob();
|
|
330
|
+
return this.currentJobId;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private resetLastCall(): void {
|
|
334
|
+
const notQueriedMessageCount = this.lastCallInfo.queried
|
|
335
|
+
? 0
|
|
336
|
+
: this.lastCallInfo.offchainEffects.filter(payload => payload[0]?.equals(OFFCHAIN_MESSAGE_IDENTIFIER)).length;
|
|
337
|
+
if (notQueriedMessageCount > 0) {
|
|
338
|
+
this.logger.warn(
|
|
339
|
+
`Dropping ${notQueriedMessageCount} unqueried offchain message(s) from the previous top-level call. ` +
|
|
340
|
+
`To deliver them, call \`env.offchain_messages()\` and forward the result to the recipient contract's ` +
|
|
341
|
+
`\`offchain_receive\` utility before issuing another top-level call. To intentionally discard, assign ` +
|
|
342
|
+
`to \`let _ = env.offchain_messages()\` to silence this warning.`,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
this.lastCallInfo = emptyLastCallState();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
recordOffchainEffect(data: Fr[]): void {
|
|
349
|
+
this.lastCallInfo.offchainEffects.push(data);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private setLastCallContext(txHash: Fr, anchorBlockTimestamp: bigint): void {
|
|
353
|
+
this.lastCallInfo.txHash = txHash;
|
|
354
|
+
this.lastCallInfo.anchorBlockTimestamp = anchorBlockTimestamp;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T> {
|
|
358
|
+
this.resetLastCall();
|
|
359
|
+
// Capture the anchor *before* `work` runs: private/public executor calls mine a new block as a
|
|
360
|
+
// side effect, and that block's timestamp should not be attributed to this call's anchor.
|
|
361
|
+
const anchorBlockTimestamp = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
|
|
362
|
+
const { result, txHash } = await work();
|
|
363
|
+
this.setLastCallContext(txHash ?? Fr.ZERO, anchorBlockTimestamp);
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
getLastCallOffchainEffects(): { effects: Fr[][] } {
|
|
368
|
+
this.lastCallInfo.queried = true;
|
|
369
|
+
return { effects: this.lastCallInfo.offchainEffects };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint } {
|
|
373
|
+
const { txHash, anchorBlockTimestamp } = this.lastCallInfo;
|
|
374
|
+
return { txHash, anchorBlockTimestamp };
|
|
375
|
+
}
|
|
376
|
+
|
|
233
377
|
async enterTopLevelState() {
|
|
234
378
|
switch (this.state.name) {
|
|
235
379
|
case 'PRIVATE': {
|
|
@@ -252,13 +396,21 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
252
396
|
}
|
|
253
397
|
}
|
|
254
398
|
|
|
399
|
+
// Commit all staged stores from the job that was just completed, then begin a new job
|
|
400
|
+
await this.cycleJob();
|
|
401
|
+
|
|
255
402
|
this.oracleHandler = new TXEOracleTopLevelContext(
|
|
256
403
|
this.stateMachine,
|
|
257
|
-
this.
|
|
404
|
+
this.contractStore,
|
|
405
|
+
this.noteStore,
|
|
258
406
|
this.keyStore,
|
|
259
|
-
this.
|
|
260
|
-
this.
|
|
261
|
-
this.
|
|
407
|
+
this.addressStore,
|
|
408
|
+
this.accountStore,
|
|
409
|
+
this.senderTaggingStore,
|
|
410
|
+
this.recipientTaggingStore,
|
|
411
|
+
this.senderAddressBookStore,
|
|
412
|
+
this.capsuleStore,
|
|
413
|
+
this.privateEventStore,
|
|
262
414
|
this.nextBlockTimestamp,
|
|
263
415
|
this.version,
|
|
264
416
|
this.chainId,
|
|
@@ -271,25 +423,24 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
271
423
|
|
|
272
424
|
async enterPrivateState(
|
|
273
425
|
contractAddress: AztecAddress = DEFAULT_ADDRESS,
|
|
274
|
-
anchorBlockNumber?:
|
|
426
|
+
anchorBlockNumber?: BlockNumber,
|
|
275
427
|
): Promise<PrivateContextInputs> {
|
|
276
428
|
this.exitTopLevelState();
|
|
277
|
-
|
|
278
|
-
// There is no automatic message discovery and contract-driven syncing process in inlined private or utility
|
|
279
|
-
// contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
|
|
280
|
-
// we perform this. We therefore search for known nullifiers now, as otherwise notes that were nullified would not
|
|
281
|
-
// be removed from the database.
|
|
282
|
-
// TODO(#12553): make the synchronizer sync here instead and remove this
|
|
283
|
-
await this.pxeOracleInterface.syncNoteNullifiers(contractAddress);
|
|
429
|
+
this.resetLastCall();
|
|
284
430
|
|
|
285
431
|
// Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
|
|
286
432
|
// build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
|
|
287
433
|
// a single transaction with the effects of what was done in the test.
|
|
288
434
|
const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
|
|
435
|
+
|
|
436
|
+
await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
|
|
437
|
+
contractAddress,
|
|
438
|
+
await this.keyStore.getAccounts(),
|
|
439
|
+
);
|
|
289
440
|
const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
|
|
290
441
|
|
|
291
442
|
const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
|
|
292
|
-
blockNumber: latestBlock!.globalVariables.blockNumber + 1,
|
|
443
|
+
blockNumber: BlockNumber(latestBlock!.globalVariables.blockNumber + 1),
|
|
293
444
|
timestamp: this.nextBlockTimestamp,
|
|
294
445
|
version: this.version,
|
|
295
446
|
chainId: this.chainId,
|
|
@@ -300,38 +451,59 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
300
451
|
const noteCache = new ExecutionNoteCache(protocolNullifier);
|
|
301
452
|
const taggingIndexCache = new ExecutionTaggingIndexCache();
|
|
302
453
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
new
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
454
|
+
const utilityExecutor = this.utilityExecutorForContractSync(anchorBlock);
|
|
455
|
+
this.oracleHandler = new PrivateExecutionOracle({
|
|
456
|
+
argsHash: Fr.ZERO,
|
|
457
|
+
txContext: new TxContext(this.chainId, this.version, GasSettings.empty()),
|
|
458
|
+
callContext: new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
|
|
459
|
+
anchorBlockHeader: anchorBlock!,
|
|
460
|
+
utilityExecutor,
|
|
461
|
+
authWitnesses: [],
|
|
462
|
+
capsules: [],
|
|
463
|
+
executionCache: new HashedValuesCache(),
|
|
311
464
|
noteCache,
|
|
312
465
|
taggingIndexCache,
|
|
313
|
-
this.
|
|
314
|
-
|
|
466
|
+
contractStore: this.contractStore,
|
|
467
|
+
noteStore: this.noteStore,
|
|
468
|
+
keyStore: this.keyStore,
|
|
469
|
+
addressStore: this.addressStore,
|
|
470
|
+
aztecNode: this.stateMachine.node,
|
|
471
|
+
senderTaggingStore: this.senderTaggingStore,
|
|
472
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
473
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
474
|
+
capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
|
|
475
|
+
privateEventStore: this.privateEventStore,
|
|
476
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
477
|
+
l2TipsStore: this.stateMachine.node,
|
|
478
|
+
jobId: this.currentJobId,
|
|
479
|
+
scopes: await this.keyStore.getAccounts(),
|
|
480
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
481
|
+
});
|
|
315
482
|
|
|
316
483
|
// We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
|
|
317
484
|
// data) in order to refer to it later, mimicking the way this object is used by the ContractFunctionSimulator. The
|
|
318
485
|
// difference resides in that the simulator has all information needed in order to run the simulation, while ours
|
|
319
486
|
// will be ongoing as the different oracles will be invoked from the Noir test, until eventually the private
|
|
320
487
|
// execution finishes.
|
|
321
|
-
this.state = { name: 'PRIVATE', nextBlockGlobalVariables,
|
|
488
|
+
this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
|
|
322
489
|
this.logger.debug(`Entered state ${this.state.name}`);
|
|
323
490
|
|
|
491
|
+
// Record the *resolved* anchor's timestamp — if the caller pinned the anchor to a past block
|
|
492
|
+
// via `anchorBlockNumber`, "latest" would be the wrong anchor for offchain-message semantics.
|
|
493
|
+
this.setLastCallContext(Fr.ZERO, anchorBlock!.globalVariables.timestamp);
|
|
494
|
+
|
|
324
495
|
return (this.oracleHandler as PrivateExecutionOracle).getPrivateContextInputs();
|
|
325
496
|
}
|
|
326
497
|
|
|
327
498
|
async enterPublicState(contractAddress?: AztecAddress) {
|
|
328
499
|
this.exitTopLevelState();
|
|
500
|
+
this.resetLastCall();
|
|
329
501
|
|
|
330
502
|
// The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
|
|
331
503
|
// the test. The block therefore gets the *next* block number and timestamp.
|
|
332
|
-
const
|
|
504
|
+
const latestHeader = (await this.stateMachine.node.getBlockHeader('latest'))!;
|
|
333
505
|
const globalVariables = makeGlobalVariables(undefined, {
|
|
334
|
-
blockNumber:
|
|
506
|
+
blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
|
|
335
507
|
timestamp: this.nextBlockTimestamp,
|
|
336
508
|
version: this.version,
|
|
337
509
|
chainId: this.chainId,
|
|
@@ -346,22 +518,55 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
346
518
|
|
|
347
519
|
this.state = { name: 'PUBLIC' };
|
|
348
520
|
this.logger.debug(`Entered state ${this.state.name}`);
|
|
521
|
+
|
|
522
|
+
// Public state is anchored at the latest block.
|
|
523
|
+
this.setLastCallContext(Fr.ZERO, latestHeader.globalVariables.timestamp);
|
|
349
524
|
}
|
|
350
525
|
|
|
351
526
|
async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
|
|
352
527
|
this.exitTopLevelState();
|
|
528
|
+
this.resetLastCall();
|
|
529
|
+
|
|
530
|
+
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
353
531
|
|
|
354
532
|
// There is no automatic message discovery and contract-driven syncing process in inlined private or utility
|
|
355
533
|
// contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
|
|
356
534
|
// we perform this. We therefore search for known nullifiers now, as otherwise notes that were nullified would not
|
|
357
535
|
// be removed from the database.
|
|
358
536
|
// TODO(#12553): make the synchronizer sync here instead and remove this
|
|
359
|
-
await
|
|
360
|
-
|
|
361
|
-
|
|
537
|
+
await new NoteService(
|
|
538
|
+
this.noteStore,
|
|
539
|
+
this.stateMachine.node,
|
|
540
|
+
anchorBlockHeader,
|
|
541
|
+
this.currentJobId,
|
|
542
|
+
).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
|
|
543
|
+
|
|
544
|
+
this.oracleHandler = new UtilityExecutionOracle({
|
|
545
|
+
contractAddress,
|
|
546
|
+
authWitnesses: [],
|
|
547
|
+
capsules: [],
|
|
548
|
+
anchorBlockHeader,
|
|
549
|
+
contractStore: this.contractStore,
|
|
550
|
+
noteStore: this.noteStore,
|
|
551
|
+
keyStore: this.keyStore,
|
|
552
|
+
addressStore: this.addressStore,
|
|
553
|
+
aztecNode: this.stateMachine.node,
|
|
554
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
555
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
556
|
+
capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
|
|
557
|
+
privateEventStore: this.privateEventStore,
|
|
558
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
559
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
560
|
+
l2TipsStore: this.stateMachine.node,
|
|
561
|
+
jobId: this.currentJobId,
|
|
562
|
+
scopes: await this.keyStore.getAccounts(),
|
|
563
|
+
});
|
|
362
564
|
|
|
363
565
|
this.state = { name: 'UTILITY' };
|
|
364
566
|
this.logger.debug(`Entered state ${this.state.name}`);
|
|
567
|
+
|
|
568
|
+
// Utility state anchors at whatever the anchor block store is pointing to (tracked as latest).
|
|
569
|
+
this.setLastCallContext(Fr.ZERO, anchorBlockHeader.globalVariables.timestamp);
|
|
365
570
|
}
|
|
366
571
|
|
|
367
572
|
private exitTopLevelState() {
|
|
@@ -371,8 +576,8 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
371
576
|
|
|
372
577
|
// Note that while all public and private contexts do is build a single block that we then process when exiting
|
|
373
578
|
// those, the top level context performs a large number of actions not captured in the following 'close' call. Among
|
|
374
|
-
// others, it will create empty blocks (via `
|
|
375
|
-
// `
|
|
579
|
+
// others, it will create empty blocks (via `advanceBlocksBy` and `deploy`), create blocks with transactions via
|
|
580
|
+
// `privateCallNewFlow` and `publicCallNewFlow`, add accounts to PXE via `addAccount`, etc. This is a
|
|
376
581
|
// slight inconsistency in the working model of this class, but is not too bad.
|
|
377
582
|
// TODO: it's quite unfortunate that we need to capture the authwits created to later pass them again when the top
|
|
378
583
|
// level context is re-created. This is because authwits create a temporary utility context that'd otherwise reset
|
|
@@ -392,21 +597,14 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
392
597
|
|
|
393
598
|
// We rely on the note cache to determine the effects of the transaction. This is incomplete as it doesn't private
|
|
394
599
|
// logs (other effects like enqueued public calls don't need to be considered since those are not allowed).
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
this.state.protocolNullifier,
|
|
398
|
-
this.state.nextBlockGlobalVariables.blockNumber,
|
|
399
|
-
);
|
|
600
|
+
|
|
601
|
+
const txEffect = await makeTxEffect(this.state.noteCache, this.state.nextBlockGlobalVariables.blockNumber);
|
|
400
602
|
|
|
401
603
|
// We build a block holding just this transaction
|
|
402
604
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
403
605
|
await insertTxEffectIntoWorldTrees(txEffect, forkedWorldTrees);
|
|
404
606
|
|
|
405
|
-
const block =
|
|
406
|
-
makeAppendOnlyTreeSnapshot(),
|
|
407
|
-
await makeTXEBlockHeader(forkedWorldTrees, this.state.nextBlockGlobalVariables),
|
|
408
|
-
new Body([txEffect]),
|
|
409
|
-
);
|
|
607
|
+
const block = await makeTXEBlock(forkedWorldTrees, this.state.nextBlockGlobalVariables, [txEffect]);
|
|
410
608
|
await this.stateMachine.handleL2Block(block);
|
|
411
609
|
|
|
412
610
|
await forkedWorldTrees.close();
|
|
@@ -431,4 +629,52 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
431
629
|
throw new Error(`Expected to be in state 'UTILITY', but got '${this.state.name}' instead`);
|
|
432
630
|
}
|
|
433
631
|
}
|
|
632
|
+
|
|
633
|
+
private utilityExecutorForContractSync(anchorBlock: any) {
|
|
634
|
+
return async (call: FunctionCall, scopes: AztecAddress[]) => {
|
|
635
|
+
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
636
|
+
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
637
|
+
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
try {
|
|
641
|
+
const oracle = new UtilityExecutionOracle({
|
|
642
|
+
contractAddress: call.to,
|
|
643
|
+
authWitnesses: [],
|
|
644
|
+
capsules: [],
|
|
645
|
+
anchorBlockHeader: anchorBlock!,
|
|
646
|
+
contractStore: this.contractStore,
|
|
647
|
+
noteStore: this.noteStore,
|
|
648
|
+
keyStore: this.keyStore,
|
|
649
|
+
addressStore: this.addressStore,
|
|
650
|
+
aztecNode: this.stateMachine.node,
|
|
651
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
652
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
653
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
654
|
+
privateEventStore: this.privateEventStore,
|
|
655
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
656
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
657
|
+
l2TipsStore: this.stateMachine.node,
|
|
658
|
+
jobId: this.currentJobId,
|
|
659
|
+
scopes,
|
|
660
|
+
});
|
|
661
|
+
await new WASMSimulator()
|
|
662
|
+
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
663
|
+
.catch((err: Error) => {
|
|
664
|
+
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
665
|
+
throw new ExecutionError(
|
|
666
|
+
err.message,
|
|
667
|
+
{
|
|
668
|
+
contractAddress: call.to,
|
|
669
|
+
functionSelector: call.selector,
|
|
670
|
+
},
|
|
671
|
+
extractCallStack(err, entryPointArtifact.debug),
|
|
672
|
+
{ cause: err },
|
|
673
|
+
);
|
|
674
|
+
});
|
|
675
|
+
} catch (err) {
|
|
676
|
+
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error contract data sync'));
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
}
|
|
434
680
|
}
|
package/src/util/encoding.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
1
2
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
3
3
|
import { hexToBuffer } from '@aztec/foundation/string';
|
|
4
4
|
import { type ContractArtifact, ContractArtifactSchema } from '@aztec/stdlib/abi';
|
|
5
5
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
|
+
import { BlockHash } from '@aztec/stdlib/block';
|
|
6
7
|
import { type ContractInstanceWithAddress, ContractInstanceWithAddressSchema } from '@aztec/stdlib/contract';
|
|
7
8
|
|
|
8
9
|
import { z } from 'zod';
|
|
@@ -25,6 +26,10 @@ export function addressFromSingle(obj: ForeignCallSingle) {
|
|
|
25
26
|
return new AztecAddress(fromSingle(obj));
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
export function blockHashFromSingle(obj: ForeignCallSingle) {
|
|
30
|
+
return new BlockHash(fromSingle(obj));
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
export function fromArray(obj: ForeignCallArray) {
|
|
29
34
|
return obj.map(str => Fr.fromBuffer(hexToBuffer(str)));
|
|
30
35
|
}
|