@aztec/txe 3.0.0-nightly.20250926 → 3.0.0-nightly.20250928
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/txe_oracle_top_level_context.js +2 -2
- package/dest/rpc_translator.js +4 -4
- package/dest/txe_session.d.ts +12 -14
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +125 -97
- 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/txe_oracle_top_level_context.ts +1 -1
- package/src/rpc_translator.ts +4 -4
- package/src/txe_session.ts +170 -119
- package/src/utils/tx_effect_creation.ts +37 -0
- package/dest/oracle/txe_oracle.d.ts +0 -79
- package/dest/oracle/txe_oracle.d.ts.map +0 -1
- package/dest/oracle/txe_oracle.js +0 -303
- package/src/oracle/txe_oracle.ts +0 -462
|
@@ -185,10 +185,10 @@ export class TXEOracleTopLevelContext {
|
|
|
185
185
|
const simulator = new WASMSimulator();
|
|
186
186
|
const privateExecutionOracle = new PrivateExecutionOracle(argsHash, txContext, callContext, /** Header of a block whose state is used during private execution (not the block the transaction is included in). */ blockHeader, /** List of transient auth witnesses to be used during this simulation */ Array.from(this.authwits.values()), /** List of transient auth witnesses to be used during this simulation */ [], HashedValuesCache.create([
|
|
187
187
|
new HashedValues(args, argsHash)
|
|
188
|
-
]), noteCache, this.pxeOracleInterface,
|
|
188
|
+
]), noteCache, this.pxeOracleInterface, 0, 1, undefined, undefined, /**
|
|
189
189
|
* In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
190
190
|
* contract would perform, including setting senderForTags.
|
|
191
|
-
*/ from);
|
|
191
|
+
*/ from, simulator);
|
|
192
192
|
// Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks.
|
|
193
193
|
let result;
|
|
194
194
|
let executionResult;
|
package/dest/rpc_translator.js
CHANGED
|
@@ -57,23 +57,23 @@ export class RPCTranslator {
|
|
|
57
57
|
}
|
|
58
58
|
// TXE session state transition functions - these get handled by the state handler
|
|
59
59
|
async txeSetTopLevelTXEContext() {
|
|
60
|
-
await this.stateHandler.
|
|
60
|
+
await this.stateHandler.enterTopLevelState();
|
|
61
61
|
return toForeignCallResult([]);
|
|
62
62
|
}
|
|
63
63
|
async txeSetPrivateTXEContext(foreignContractAddressIsSome, foreignContractAddressValue, foreignAnchorBlockNumberIsSome, foreignAnchorBlockNumberValue) {
|
|
64
64
|
const contractAddress = fromSingle(foreignContractAddressIsSome).toBool() ? AztecAddress.fromField(fromSingle(foreignContractAddressValue)) : undefined;
|
|
65
65
|
const anchorBlockNumber = fromSingle(foreignAnchorBlockNumberIsSome).toBool() ? fromSingle(foreignAnchorBlockNumberValue).toNumber() : undefined;
|
|
66
|
-
const privateContextInputs = await this.stateHandler.
|
|
66
|
+
const privateContextInputs = await this.stateHandler.enterPrivateState(contractAddress, anchorBlockNumber);
|
|
67
67
|
return toForeignCallResult(privateContextInputs.toFields().map(toSingle));
|
|
68
68
|
}
|
|
69
69
|
async txeSetPublicTXEContext(foreignContractAddressIsSome, foreignContractAddressValue) {
|
|
70
70
|
const contractAddress = fromSingle(foreignContractAddressIsSome).toBool() ? AztecAddress.fromField(fromSingle(foreignContractAddressValue)) : undefined;
|
|
71
|
-
await this.stateHandler.
|
|
71
|
+
await this.stateHandler.enterPublicState(contractAddress);
|
|
72
72
|
return toForeignCallResult([]);
|
|
73
73
|
}
|
|
74
74
|
async txeSetUtilityTXEContext(foreignContractAddressIsSome, foreignContractAddressValue) {
|
|
75
75
|
const contractAddress = fromSingle(foreignContractAddressIsSome).toBool() ? AztecAddress.fromField(fromSingle(foreignContractAddressValue)) : undefined;
|
|
76
|
-
await this.stateHandler.
|
|
76
|
+
await this.stateHandler.enterUtilityState(contractAddress);
|
|
77
77
|
return toForeignCallResult([]);
|
|
78
78
|
}
|
|
79
79
|
// Other oracles - these get handled by the oracle handler
|
package/dest/txe_session.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type Logger } from '@aztec/foundation/log';
|
|
|
3
3
|
import { KeyStore } from '@aztec/key-store';
|
|
4
4
|
import type { ProtocolContract } from '@aztec/protocol-contracts';
|
|
5
5
|
import { AddressDataProvider, PXEOracleInterface } from '@aztec/pxe/server';
|
|
6
|
-
import type
|
|
6
|
+
import { type IPrivateExecutionOracle, type IUtilityExecutionOracle } from '@aztec/pxe/simulator';
|
|
7
7
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
8
8
|
import { PrivateContextInputs } from '@aztec/stdlib/kernel';
|
|
9
9
|
import type { UInt32 } from '@aztec/stdlib/types';
|
|
@@ -22,10 +22,10 @@ type MethodNames<T> = {
|
|
|
22
22
|
*/
|
|
23
23
|
export type TXEOracleFunctionName = MethodNames<RPCTranslator>;
|
|
24
24
|
export interface TXESessionStateHandler {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
enterTopLevelState(): Promise<void>;
|
|
26
|
+
enterPublicState(contractAddress?: AztecAddress): Promise<void>;
|
|
27
|
+
enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: UInt32): Promise<PrivateContextInputs>;
|
|
28
|
+
enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* A `TXESession` corresponds to a Noir `#[test]` function, and handles all of its oracle calls, stores test-specific
|
|
@@ -54,16 +54,14 @@ export declare class TXESession implements TXESessionStateHandler {
|
|
|
54
54
|
* @returns The oracle return values.
|
|
55
55
|
*/
|
|
56
56
|
processFunction(functionName: TXEOracleFunctionName, inputs: ForeignCallArgs): Promise<ForeignCallResult>;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
private
|
|
62
|
-
private
|
|
63
|
-
private
|
|
57
|
+
enterTopLevelState(): Promise<void>;
|
|
58
|
+
enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: UInt32): Promise<PrivateContextInputs>;
|
|
59
|
+
enterPublicState(contractAddress?: AztecAddress): Promise<void>;
|
|
60
|
+
enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
|
|
61
|
+
private exitTopLevelState;
|
|
62
|
+
private exitPrivateState;
|
|
63
|
+
private exitPublicState;
|
|
64
64
|
private exitUtilityContext;
|
|
65
|
-
private assertState;
|
|
66
|
-
private getPrivateContextInputs;
|
|
67
65
|
}
|
|
68
66
|
export {};
|
|
69
67
|
//# sourceMappingURL=txe_session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"txe_session.d.ts","sourceRoot":"","sources":["../src/txe_session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EACL,mBAAmB,EAGnB,kBAAkB,EAGnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"txe_session.d.ts","sourceRoot":"","sources":["../src/txe_session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EACL,mBAAmB,EAGnB,kBAAkB,EAGnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAGL,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAG7B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAG3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAGvF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AA6C/E,KAAK,WAAW,CAAC,CAAC,IAAI;KACnB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK;CACjE,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,gBAAgB,CAAC,eAAe,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,CAAC,eAAe,CAAC,EAAE,YAAY,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC7G,iBAAiB,CAAC,eAAe,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAID;;;GAGG;AACH,qBAAa,UAAW,YAAW,sBAAsB;IAKrD,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,oBAAoB;IAC5B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,kBAAkB;IAlB5B,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,QAAQ,CAAuC;gBAG7C,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,eAAe,EAC7B,aAAa,EACjB,uBAAuB,GACvB,uBAAuB,GACvB,mBAAmB,GACnB,mBAAmB,EACf,oBAAoB,EAAE,uBAAuB,EAC7C,QAAQ,EAAE,QAAQ,EAClB,mBAAmB,EAAE,mBAAmB,EACxC,mBAAmB,EAAE,sBAAsB,EAC3C,OAAO,EAAE,EAAE,EACX,OAAO,EAAE,EAAE,EACX,kBAAkB,EAAE,MAAM,EAC1B,kBAAkB,EAAE,kBAAkB;WAGnC,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,EAAE;IAiEvD;;;;;OAKG;IACH,eAAe,CAAC,YAAY,EAAE,qBAAqB,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAgBnG,kBAAkB;IAuClB,iBAAiB,CACrB,eAAe,GAAE,YAA8B,EAC/C,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,oBAAoB,CAAC;IAgD1B,gBAAgB,CAAC,eAAe,CAAC,EAAE,YAAY;IAwB/C,iBAAiB,CAAC,eAAe,GAAE,YAA8B;IAgBvE,OAAO,CAAC,iBAAiB;YAiBX,gBAAgB;YAoChB,eAAe;IAS7B,OAAO,CAAC,kBAAkB;CAK3B"}
|
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
|
|
@@ -69,7 +48,9 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
|
|
|
69
48
|
this.version = version;
|
|
70
49
|
this.nextBlockTimestamp = nextBlockTimestamp;
|
|
71
50
|
this.pxeOracleInterface = pxeOracleInterface;
|
|
72
|
-
this.state =
|
|
51
|
+
this.state = {
|
|
52
|
+
name: 'TOP_LEVEL'
|
|
53
|
+
};
|
|
73
54
|
this.authwits = new Map();
|
|
74
55
|
}
|
|
75
56
|
static async init(protocolContracts) {
|
|
@@ -106,30 +87,81 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
|
|
|
106
87
|
return new RPCTranslator(this, this.oracleHandler)[functionName](...inputs);
|
|
107
88
|
} catch (error) {
|
|
108
89
|
if (error instanceof Error) {
|
|
109
|
-
throw new Error(`Execution error while processing function ${functionName} in state ${
|
|
90
|
+
throw new Error(`Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`);
|
|
110
91
|
} else {
|
|
111
|
-
throw new Error(`Unknown execution error while processing function ${functionName} in state ${
|
|
92
|
+
throw new Error(`Unknown execution error while processing function ${functionName} in state ${this.state.name}`);
|
|
112
93
|
}
|
|
113
94
|
}
|
|
114
95
|
}
|
|
115
|
-
async
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
+
}
|
|
126
121
|
}
|
|
127
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);
|
|
128
|
-
this.state =
|
|
129
|
-
|
|
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();
|
|
130
162
|
}
|
|
131
|
-
async
|
|
132
|
-
this.
|
|
163
|
+
async enterPublicState(contractAddress) {
|
|
164
|
+
this.exitTopLevelState();
|
|
133
165
|
// The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
|
|
134
166
|
// the test. The block therefore gets the *next* block number and timestamp.
|
|
135
167
|
const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest')).globalVariables.blockNumber;
|
|
@@ -140,45 +172,29 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
|
|
|
140
172
|
chainId: this.chainId
|
|
141
173
|
});
|
|
142
174
|
this.oracleHandler = new TXEOraclePublicContext(contractAddress ?? DEFAULT_ADDRESS, await this.stateMachine.synchronizer.nativeWorldStateService.fork(), getSingleTxBlockRequestHash(globalVariables.blockNumber), globalVariables);
|
|
143
|
-
this.state =
|
|
144
|
-
|
|
175
|
+
this.state = {
|
|
176
|
+
name: 'PUBLIC'
|
|
177
|
+
};
|
|
178
|
+
this.logger.debug(`Entered state ${this.state.name}`);
|
|
145
179
|
}
|
|
146
|
-
async
|
|
147
|
-
this.
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
});
|
|
159
|
-
const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
|
|
160
|
-
blockNumber: latestBlock.globalVariables.blockNumber + 1,
|
|
161
|
-
timestamp: this.nextBlockTimestamp,
|
|
162
|
-
version: this.version,
|
|
163
|
-
chainId: this.chainId
|
|
164
|
-
});
|
|
165
|
-
const privateContextInputs = await this.getPrivateContextInputs(anchorBlockGlobalVariables.blockNumber, contractAddress ?? DEFAULT_ADDRESS);
|
|
166
|
-
this.oracleHandler = await TXE.create(contractAddress ?? DEFAULT_ADDRESS, this.pxeOracleInterface, await this.stateMachine.synchronizer.nativeWorldStateService.fork(), anchorBlockGlobalVariables, nextBlockGlobalVariables, getSingleTxBlockRequestHash(nextBlockGlobalVariables.blockNumber));
|
|
167
|
-
this.state = 1;
|
|
168
|
-
this.logger.debug(`Entered state ${SessionState[this.state]}`);
|
|
169
|
-
return privateContextInputs;
|
|
170
|
-
}
|
|
171
|
-
async setUtilityContext(contractAddress) {
|
|
172
|
-
this.exitTopLevelContext();
|
|
173
|
-
// A UtilityContext is built using the latest block as a reference, mimicking what would happen if PXE had synced
|
|
174
|
-
// all the way to the tip of the chain.
|
|
175
|
-
const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
|
|
176
|
-
this.oracleHandler = await TXE.create(contractAddress ?? DEFAULT_ADDRESS, this.pxeOracleInterface, await this.stateMachine.synchronizer.nativeWorldStateService.fork(), latestBlock.globalVariables, GlobalVariables.empty(), Fr.random());
|
|
177
|
-
this.state = 3;
|
|
178
|
-
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}`);
|
|
179
193
|
}
|
|
180
|
-
|
|
181
|
-
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
|
+
}
|
|
182
198
|
// Note that while all public and private contexts do is build a single block that we then process when exiting
|
|
183
199
|
// those, the top level context performs a large number of actions not captured in the following 'close' call. Among
|
|
184
200
|
// others, it will create empty blocks (via `txeAdvanceBlocksBy` and `deploy`), create blocks with transactions via
|
|
@@ -190,27 +206,39 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
|
|
|
190
206
|
// Ideally authwits would be passed alongside a contract call instead of pre-seeded.
|
|
191
207
|
[this.nextBlockTimestamp, this.authwits] = this.oracleHandler.close();
|
|
192
208
|
}
|
|
193
|
-
async
|
|
194
|
-
this.
|
|
195
|
-
|
|
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
|
+
]));
|
|
196
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
|
+
});
|
|
197
231
|
}
|
|
198
|
-
async
|
|
199
|
-
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
|
+
}
|
|
200
236
|
const block = await this.oracleHandler.close();
|
|
201
237
|
await this.stateMachine.handleL2Block(block);
|
|
202
238
|
}
|
|
203
239
|
exitUtilityContext() {
|
|
204
|
-
this.
|
|
205
|
-
|
|
206
|
-
assertState(state) {
|
|
207
|
-
if (this.state != state) {
|
|
208
|
-
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`);
|
|
209
242
|
}
|
|
210
243
|
}
|
|
211
|
-
async getPrivateContextInputs(anchorBlockNumber, contractAddress) {
|
|
212
|
-
this.logger.info(`Creating private context for block ${anchorBlockNumber}`);
|
|
213
|
-
const sender = await AztecAddress.random();
|
|
214
|
-
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);
|
|
215
|
-
}
|
|
216
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.20250928",
|
|
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.20250928",
|
|
65
|
+
"@aztec/archiver": "3.0.0-nightly.20250928",
|
|
66
|
+
"@aztec/aztec-node": "3.0.0-nightly.20250928",
|
|
67
|
+
"@aztec/aztec.js": "3.0.0-nightly.20250928",
|
|
68
|
+
"@aztec/bb-prover": "3.0.0-nightly.20250928",
|
|
69
|
+
"@aztec/constants": "3.0.0-nightly.20250928",
|
|
70
|
+
"@aztec/foundation": "3.0.0-nightly.20250928",
|
|
71
|
+
"@aztec/key-store": "3.0.0-nightly.20250928",
|
|
72
|
+
"@aztec/kv-store": "3.0.0-nightly.20250928",
|
|
73
|
+
"@aztec/protocol-contracts": "3.0.0-nightly.20250928",
|
|
74
|
+
"@aztec/pxe": "3.0.0-nightly.20250928",
|
|
75
|
+
"@aztec/simulator": "3.0.0-nightly.20250928",
|
|
76
|
+
"@aztec/stdlib": "3.0.0-nightly.20250928",
|
|
77
|
+
"@aztec/world-state": "3.0.0-nightly.20250928",
|
|
78
78
|
"zod": "^3.23.8"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
@@ -303,7 +303,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
303
303
|
HashedValuesCache.create([new HashedValues(args, argsHash)]),
|
|
304
304
|
noteCache,
|
|
305
305
|
this.pxeOracleInterface,
|
|
306
|
-
simulator,
|
|
307
306
|
0,
|
|
308
307
|
1,
|
|
309
308
|
undefined, // log
|
|
@@ -313,6 +312,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
313
312
|
* contract would perform, including setting senderForTags.
|
|
314
313
|
*/
|
|
315
314
|
from,
|
|
315
|
+
simulator,
|
|
316
316
|
);
|
|
317
317
|
|
|
318
318
|
// Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks.
|
package/src/rpc_translator.ts
CHANGED
|
@@ -97,7 +97,7 @@ export class RPCTranslator {
|
|
|
97
97
|
// TXE session state transition functions - these get handled by the state handler
|
|
98
98
|
|
|
99
99
|
async txeSetTopLevelTXEContext() {
|
|
100
|
-
await this.stateHandler.
|
|
100
|
+
await this.stateHandler.enterTopLevelState();
|
|
101
101
|
|
|
102
102
|
return toForeignCallResult([]);
|
|
103
103
|
}
|
|
@@ -116,7 +116,7 @@ export class RPCTranslator {
|
|
|
116
116
|
? fromSingle(foreignAnchorBlockNumberValue).toNumber()
|
|
117
117
|
: undefined;
|
|
118
118
|
|
|
119
|
-
const privateContextInputs = await this.stateHandler.
|
|
119
|
+
const privateContextInputs = await this.stateHandler.enterPrivateState(contractAddress, anchorBlockNumber);
|
|
120
120
|
|
|
121
121
|
return toForeignCallResult(privateContextInputs.toFields().map(toSingle));
|
|
122
122
|
}
|
|
@@ -129,7 +129,7 @@ export class RPCTranslator {
|
|
|
129
129
|
? AztecAddress.fromField(fromSingle(foreignContractAddressValue))
|
|
130
130
|
: undefined;
|
|
131
131
|
|
|
132
|
-
await this.stateHandler.
|
|
132
|
+
await this.stateHandler.enterPublicState(contractAddress);
|
|
133
133
|
|
|
134
134
|
return toForeignCallResult([]);
|
|
135
135
|
}
|
|
@@ -142,7 +142,7 @@ export class RPCTranslator {
|
|
|
142
142
|
? AztecAddress.fromField(fromSingle(foreignContractAddressValue))
|
|
143
143
|
: undefined;
|
|
144
144
|
|
|
145
|
-
await this.stateHandler.
|
|
145
|
+
await this.stateHandler.enterUtilityState(contractAddress);
|
|
146
146
|
|
|
147
147
|
return toForeignCallResult([]);
|
|
148
148
|
}
|