@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
|
@@ -1,35 +1,41 @@
|
|
|
1
|
-
import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
2
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { Schnorr } from '@aztec/foundation/crypto/schnorr';
|
|
4
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
5
|
import { LogLevels, applyStringFormatting, createLogger } from '@aztec/foundation/log';
|
|
5
6
|
import { TestDateProvider } from '@aztec/foundation/timer';
|
|
6
|
-
import {
|
|
7
|
+
import { CapsuleService, ORACLE_VERSION_MAJOR, enrichPublicSimulationError } from '@aztec/pxe/server';
|
|
7
8
|
import { ExecutionNoteCache, ExecutionTaggingIndexCache, HashedValuesCache, Oracle, PrivateExecutionOracle, UtilityExecutionOracle, executePrivateFunction, generateSimulatedProvingResult } from '@aztec/pxe/simulator';
|
|
8
9
|
import { ExecutionError, WASMSimulator, createSimulationError, extractCallStack, resolveAssertionMessageFromError, toACVMWitness, witnessMapToFields } from '@aztec/simulator/client';
|
|
9
|
-
import { GuardedMerkleTreeOperations, PublicContractsDB, PublicProcessor
|
|
10
|
-
import { FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
|
|
10
|
+
import { CppPublicTxSimulator, GuardedMerkleTreeOperations, PublicContractsDB, PublicProcessor } from '@aztec/simulator/server';
|
|
11
|
+
import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
|
|
11
12
|
import { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
12
13
|
import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
13
14
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
14
|
-
import { Body, L2Block } from '@aztec/stdlib/block';
|
|
15
15
|
import { computePartialAddress } from '@aztec/stdlib/contract';
|
|
16
|
-
import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
|
|
16
|
+
import { FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT, Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
|
|
17
17
|
import { computeCalldataHash, computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
|
|
18
18
|
import { PartialPrivateTailPublicInputsForPublic, PrivateKernelTailCircuitPublicInputs, PrivateToPublicAccumulatedData, PublicCallRequest } from '@aztec/stdlib/kernel';
|
|
19
19
|
import { ChonkProof } from '@aztec/stdlib/proofs';
|
|
20
|
-
import {
|
|
20
|
+
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
21
21
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
22
22
|
import { CallContext, HashedValues, PrivateExecutionResult, Tx, TxConstantData, TxContext, TxEffect, TxHash, collectNested } from '@aztec/stdlib/tx';
|
|
23
23
|
import { ForkCheckpoint } from '@aztec/world-state';
|
|
24
|
+
import { DEFAULT_ADDRESS } from '../constants.js';
|
|
24
25
|
import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
|
|
25
|
-
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees,
|
|
26
|
+
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
|
|
26
27
|
export class TXEOracleTopLevelContext {
|
|
27
28
|
stateMachine;
|
|
28
|
-
|
|
29
|
+
contractStore;
|
|
30
|
+
noteStore;
|
|
29
31
|
keyStore;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
addressStore;
|
|
33
|
+
accountStore;
|
|
34
|
+
senderTaggingStore;
|
|
35
|
+
recipientTaggingStore;
|
|
36
|
+
senderAddressBookStore;
|
|
37
|
+
capsuleStore;
|
|
38
|
+
privateEventStore;
|
|
33
39
|
nextBlockTimestamp;
|
|
34
40
|
version;
|
|
35
41
|
chainId;
|
|
@@ -37,13 +43,18 @@ export class TXEOracleTopLevelContext {
|
|
|
37
43
|
isMisc;
|
|
38
44
|
isTxe;
|
|
39
45
|
logger;
|
|
40
|
-
constructor(stateMachine,
|
|
46
|
+
constructor(stateMachine, contractStore, noteStore, keyStore, addressStore, accountStore, senderTaggingStore, recipientTaggingStore, senderAddressBookStore, capsuleStore, privateEventStore, nextBlockTimestamp, version, chainId, authwits){
|
|
41
47
|
this.stateMachine = stateMachine;
|
|
42
|
-
this.
|
|
48
|
+
this.contractStore = contractStore;
|
|
49
|
+
this.noteStore = noteStore;
|
|
43
50
|
this.keyStore = keyStore;
|
|
44
|
-
this.
|
|
45
|
-
this.
|
|
46
|
-
this.
|
|
51
|
+
this.addressStore = addressStore;
|
|
52
|
+
this.accountStore = accountStore;
|
|
53
|
+
this.senderTaggingStore = senderTaggingStore;
|
|
54
|
+
this.recipientTaggingStore = recipientTaggingStore;
|
|
55
|
+
this.senderAddressBookStore = senderAddressBookStore;
|
|
56
|
+
this.capsuleStore = capsuleStore;
|
|
57
|
+
this.privateEventStore = privateEventStore;
|
|
47
58
|
this.nextBlockTimestamp = nextBlockTimestamp;
|
|
48
59
|
this.version = version;
|
|
49
60
|
this.chainId = chainId;
|
|
@@ -53,37 +64,52 @@ export class TXEOracleTopLevelContext {
|
|
|
53
64
|
this.logger = createLogger('txe:top_level_context');
|
|
54
65
|
this.logger.debug('Entering Top Level Context');
|
|
55
66
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
67
|
+
contractOracleVersion;
|
|
68
|
+
assertCompatibleOracleVersion(major, minor) {
|
|
69
|
+
if (major !== ORACLE_VERSION_MAJOR) {
|
|
70
|
+
const hint = major > ORACLE_VERSION_MAJOR ? 'The contract was compiled with a newer version of Aztec.nr than this aztec cli version supports. Upgrade your aztec cli version to a compatible version.' : 'The contract was compiled with an older version of Aztec.nr than this aztec cli version supports. Recompile the contract with a compatible version of Aztec.nr.';
|
|
71
|
+
throw new Error(`Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`);
|
|
59
72
|
}
|
|
73
|
+
this.contractOracleVersion = {
|
|
74
|
+
major,
|
|
75
|
+
minor
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
|
|
79
|
+
nonOracleFunctionGetContractOracleVersion() {
|
|
80
|
+
return this.contractOracleVersion;
|
|
60
81
|
}
|
|
61
82
|
// This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
|
|
62
83
|
// setup.
|
|
63
|
-
|
|
84
|
+
getRandomField() {
|
|
64
85
|
return Fr.random();
|
|
65
86
|
}
|
|
66
87
|
// We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
|
|
67
|
-
|
|
88
|
+
log(level, message, fields) {
|
|
68
89
|
if (!LogLevels[level]) {
|
|
69
|
-
throw new Error(`Invalid
|
|
90
|
+
throw new Error(`Invalid log level: ${level}`);
|
|
70
91
|
}
|
|
71
92
|
const levelName = LogLevels[level];
|
|
72
93
|
this.logger[levelName](`${applyStringFormatting(message, fields)}`, {
|
|
73
94
|
module: `${this.logger.module}:debug_log`
|
|
74
95
|
});
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
}
|
|
98
|
+
getDefaultAddress() {
|
|
99
|
+
return DEFAULT_ADDRESS;
|
|
75
100
|
}
|
|
76
|
-
async
|
|
77
|
-
return await this.getLastBlockNumber() + 1;
|
|
101
|
+
async getNextBlockNumber() {
|
|
102
|
+
return BlockNumber(await this.getLastBlockNumber() + 1);
|
|
78
103
|
}
|
|
79
|
-
|
|
104
|
+
getNextBlockTimestamp() {
|
|
80
105
|
return Promise.resolve(this.nextBlockTimestamp);
|
|
81
106
|
}
|
|
82
|
-
async
|
|
107
|
+
async getLastBlockTimestamp() {
|
|
83
108
|
return (await this.stateMachine.node.getBlockHeader('latest')).globalVariables.timestamp;
|
|
84
109
|
}
|
|
85
|
-
async
|
|
86
|
-
const
|
|
110
|
+
async getLastTxEffects() {
|
|
111
|
+
const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
|
|
112
|
+
const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
|
|
87
113
|
if (block.body.txEffects.length != 1) {
|
|
88
114
|
// Note that calls like env.mine() will result in blocks with no transactions, hitting this
|
|
89
115
|
throw new Error(`Expected a single transaction in the last block, found ${block.body.txEffects.length}`);
|
|
@@ -95,17 +121,39 @@ export class TXEOracleTopLevelContext {
|
|
|
95
121
|
nullifiers: txEffects.nullifiers
|
|
96
122
|
};
|
|
97
123
|
}
|
|
98
|
-
async
|
|
124
|
+
async syncContractNonOracleMethod(contractAddress, scope, jobId) {
|
|
125
|
+
if (contractAddress.equals(DEFAULT_ADDRESS)) {
|
|
126
|
+
this.logger.debug(`Skipping sync in getPrivateEvents because the events correspond to the default address.`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
130
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(contractAddress, null, async (call, execScopes)=>{
|
|
131
|
+
await this.executeUtilityCall(call, execScopes, jobId);
|
|
132
|
+
}, blockHeader, jobId, [
|
|
133
|
+
scope
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
async getPrivateEvents(selector, contractAddress, scope) {
|
|
137
|
+
return (await this.privateEventStore.getPrivateEvents(selector, {
|
|
138
|
+
contractAddress,
|
|
139
|
+
scopes: [
|
|
140
|
+
scope
|
|
141
|
+
],
|
|
142
|
+
fromBlock: 0,
|
|
143
|
+
toBlock: await this.getLastBlockNumber() + 1
|
|
144
|
+
})).map((e)=>e.packedEvent);
|
|
145
|
+
}
|
|
146
|
+
async advanceBlocksBy(blocks) {
|
|
99
147
|
this.logger.debug(`time traveling ${blocks} blocks`);
|
|
100
148
|
for(let i = 0; i < blocks; i++){
|
|
101
149
|
await this.mineBlock();
|
|
102
150
|
}
|
|
103
151
|
}
|
|
104
|
-
|
|
152
|
+
advanceTimestampBy(duration) {
|
|
105
153
|
this.logger.debug(`time traveling ${duration} seconds`);
|
|
106
154
|
this.nextBlockTimestamp += duration;
|
|
107
155
|
}
|
|
108
|
-
async
|
|
156
|
+
async deploy(artifact, instance, secret) {
|
|
109
157
|
// Emit deployment nullifier
|
|
110
158
|
await this.mineBlock({
|
|
111
159
|
nullifiers: [
|
|
@@ -113,34 +161,34 @@ export class TXEOracleTopLevelContext {
|
|
|
113
161
|
]
|
|
114
162
|
});
|
|
115
163
|
if (!secret.equals(Fr.ZERO)) {
|
|
116
|
-
await this.
|
|
164
|
+
await this.addAccount(artifact, instance, secret);
|
|
117
165
|
} else {
|
|
118
|
-
await this.
|
|
119
|
-
await this.
|
|
166
|
+
await this.contractStore.addContractInstance(instance);
|
|
167
|
+
await this.contractStore.addContractArtifact(artifact);
|
|
120
168
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
121
169
|
}
|
|
122
170
|
}
|
|
123
|
-
async
|
|
171
|
+
async addAccount(artifact, instance, secret) {
|
|
124
172
|
const partialAddress = await computePartialAddress(instance);
|
|
125
173
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
126
|
-
await this.
|
|
127
|
-
await this.
|
|
174
|
+
await this.contractStore.addContractInstance(instance);
|
|
175
|
+
await this.contractStore.addContractArtifact(artifact);
|
|
128
176
|
const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
|
|
129
|
-
await this.
|
|
130
|
-
await this.
|
|
177
|
+
await this.accountStore.setAccount(completeAddress.address, completeAddress);
|
|
178
|
+
await this.addressStore.addCompleteAddress(completeAddress);
|
|
131
179
|
this.logger.debug(`Created account ${completeAddress.address}`);
|
|
132
180
|
return completeAddress;
|
|
133
181
|
}
|
|
134
|
-
async
|
|
135
|
-
// This is a
|
|
182
|
+
async createAccount(secret) {
|
|
183
|
+
// This is a foot gun !
|
|
136
184
|
const completeAddress = await this.keyStore.addAccount(secret, secret);
|
|
137
|
-
await this.
|
|
138
|
-
await this.
|
|
185
|
+
await this.accountStore.setAccount(completeAddress.address, completeAddress);
|
|
186
|
+
await this.addressStore.addCompleteAddress(completeAddress);
|
|
139
187
|
this.logger.debug(`Created account ${completeAddress.address}`);
|
|
140
188
|
return completeAddress;
|
|
141
189
|
}
|
|
142
|
-
async
|
|
143
|
-
const account = await this.
|
|
190
|
+
async addAuthWitness(address, messageHash) {
|
|
191
|
+
const account = await this.accountStore.getAccount(address);
|
|
144
192
|
const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
|
|
145
193
|
const schnorr = new Schnorr();
|
|
146
194
|
const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
|
|
@@ -150,7 +198,7 @@ export class TXEOracleTopLevelContext {
|
|
|
150
198
|
this.authwits.set(authWitness.requestHash.toString(), authWitness);
|
|
151
199
|
}
|
|
152
200
|
async mineBlock(options = {}) {
|
|
153
|
-
const blockNumber = await this.
|
|
201
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
154
202
|
const txEffect = TxEffect.empty();
|
|
155
203
|
txEffect.nullifiers = [
|
|
156
204
|
getSingleTxBlockRequestHash(blockNumber),
|
|
@@ -159,42 +207,87 @@ export class TXEOracleTopLevelContext {
|
|
|
159
207
|
txEffect.txHash = new TxHash(new Fr(blockNumber));
|
|
160
208
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
161
209
|
await insertTxEffectIntoWorldTrees(txEffect, forkedWorldTrees);
|
|
162
|
-
const
|
|
210
|
+
const globals = makeGlobalVariables(undefined, {
|
|
163
211
|
blockNumber,
|
|
164
212
|
timestamp: this.nextBlockTimestamp,
|
|
165
213
|
version: this.version,
|
|
166
214
|
chainId: this.chainId
|
|
167
|
-
})
|
|
215
|
+
});
|
|
216
|
+
const block = await makeTXEBlock(forkedWorldTrees, globals, [
|
|
168
217
|
txEffect
|
|
169
|
-
])
|
|
218
|
+
]);
|
|
170
219
|
await forkedWorldTrees.close();
|
|
171
220
|
this.logger.info(`Created block ${blockNumber} with timestamp ${block.header.globalVariables.timestamp}`);
|
|
172
221
|
await this.stateMachine.handleL2Block(block);
|
|
173
222
|
}
|
|
174
|
-
async
|
|
175
|
-
this.logger.verbose(`Executing external function ${await this.
|
|
176
|
-
const artifact = await this.
|
|
223
|
+
async privateCallNewFlow(from, targetContractAddress = AztecAddress.zero(), functionSelector = FunctionSelector.empty(), args, argsHash = Fr.zero(), isStaticCall = false, jobId) {
|
|
224
|
+
this.logger.verbose(`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`);
|
|
225
|
+
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
177
226
|
if (!artifact) {
|
|
178
227
|
const message = functionSelector.equals(await FunctionSelector.fromSignature('verify_private_authwit(Field)')) ? 'Found no account contract artifact for a private authwit check - use `create_contract_account` instead of `create_light_account` for authwit support.' : 'Function Artifact does not exist';
|
|
179
228
|
throw new Error(message);
|
|
180
229
|
}
|
|
181
|
-
|
|
230
|
+
// When `from` is the zero address (e.g. when deploying a new account contract), we return an
|
|
231
|
+
// empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
|
|
232
|
+
const effectiveScopes = from.isZero() ? [] : [
|
|
233
|
+
from
|
|
234
|
+
];
|
|
235
|
+
// Sync notes before executing private function to discover notes from previous transactions
|
|
236
|
+
const utilityExecutor = async (call, execScopes)=>{
|
|
237
|
+
await this.executeUtilityCall(call, execScopes, jobId);
|
|
238
|
+
};
|
|
239
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
240
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, utilityExecutor, blockHeader, jobId, effectiveScopes);
|
|
241
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
182
242
|
const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
|
|
183
|
-
const gasLimits = new Gas(
|
|
184
|
-
const teardownGasLimits = new Gas(
|
|
243
|
+
const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
|
|
244
|
+
const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
|
|
185
245
|
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
186
246
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
187
|
-
const blockHeader = await this.pxeOracleInterface.getAnchorBlockHeader();
|
|
188
247
|
const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
|
|
189
248
|
const noteCache = new ExecutionNoteCache(protocolNullifier);
|
|
249
|
+
// In production, the account contract sets the min revertible counter before calling the app function.
|
|
250
|
+
// Since TXE bypasses the account contract, we simulate this by setting minRevertibleSideEffectCounter to 1,
|
|
251
|
+
// marking all side effects as revertible.
|
|
252
|
+
const minRevertibleSideEffectCounter = 1;
|
|
253
|
+
await noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
|
|
190
254
|
const taggingIndexCache = new ExecutionTaggingIndexCache();
|
|
191
255
|
const simulator = new WASMSimulator();
|
|
192
|
-
const privateExecutionOracle = new PrivateExecutionOracle(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
256
|
+
const privateExecutionOracle = new PrivateExecutionOracle({
|
|
257
|
+
argsHash,
|
|
258
|
+
txContext,
|
|
259
|
+
callContext,
|
|
260
|
+
anchorBlockHeader: blockHeader,
|
|
261
|
+
utilityExecutor,
|
|
262
|
+
authWitnesses: Array.from(this.authwits.values()),
|
|
263
|
+
capsules: [],
|
|
264
|
+
executionCache: HashedValuesCache.create([
|
|
265
|
+
new HashedValues(args, argsHash)
|
|
266
|
+
]),
|
|
267
|
+
noteCache,
|
|
268
|
+
taggingIndexCache,
|
|
269
|
+
contractStore: this.contractStore,
|
|
270
|
+
noteStore: this.noteStore,
|
|
271
|
+
keyStore: this.keyStore,
|
|
272
|
+
addressStore: this.addressStore,
|
|
273
|
+
aztecNode: this.stateMachine.node,
|
|
274
|
+
senderTaggingStore: this.senderTaggingStore,
|
|
275
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
276
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
277
|
+
capsuleService: new CapsuleService(this.capsuleStore, effectiveScopes),
|
|
278
|
+
privateEventStore: this.privateEventStore,
|
|
279
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
280
|
+
jobId,
|
|
281
|
+
totalPublicCalldataCount: 0,
|
|
282
|
+
sideEffectCounter: minRevertibleSideEffectCounter,
|
|
283
|
+
scopes: effectiveScopes,
|
|
284
|
+
// In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
285
|
+
// contract would perform, including setting senderForTags.
|
|
286
|
+
senderForTags: from,
|
|
287
|
+
simulator,
|
|
288
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
289
|
+
l2TipsStore: this.stateMachine.node
|
|
290
|
+
});
|
|
198
291
|
// 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.
|
|
199
292
|
let result;
|
|
200
293
|
let executionResult;
|
|
@@ -206,21 +299,19 @@ export class TXEOracleTopLevelContext {
|
|
|
206
299
|
r.publicInputs.publicTeardownCallRequest
|
|
207
300
|
]));
|
|
208
301
|
const publicFunctionsCalldata = await Promise.all(publicCallRequests.map(async (r)=>{
|
|
209
|
-
const calldata = await privateExecutionOracle.
|
|
302
|
+
const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
|
|
210
303
|
return new HashedValues(calldata, r.calldataHash);
|
|
211
304
|
}));
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
result = new PrivateExecutionResult(executionResult, Fr.ZERO, publicFunctionsCalldata);
|
|
305
|
+
noteCache.finish();
|
|
306
|
+
const nonceGenerator = noteCache.getNonceGenerator();
|
|
307
|
+
result = new PrivateExecutionResult(executionResult, nonceGenerator, publicFunctionsCalldata);
|
|
216
308
|
} catch (err) {
|
|
217
309
|
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
218
310
|
}
|
|
219
|
-
// According to the protocol rules,
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
const
|
|
223
|
-
const { publicInputs } = await generateSimulatedProvingResult(result, nonceGenerator, this.contractDataProvider);
|
|
311
|
+
// According to the protocol rules, there must be at least one nullifier in the tx. The first nullifier is used as
|
|
312
|
+
// the nonce generator for the note hashes.
|
|
313
|
+
// We pass the non-zero minRevertibleSideEffectCounter to make sure the side effects are split correctly.
|
|
314
|
+
const { publicInputs } = await generateSimulatedProvingResult(result, (addr, sel)=>this.contractStore.getDebugFunctionName(addr, sel), this.stateMachine.node, minRevertibleSideEffectCounter);
|
|
224
315
|
const globals = makeGlobalVariables();
|
|
225
316
|
globals.blockNumber = blockNumber;
|
|
226
317
|
globals.timestamp = this.nextBlockTimestamp;
|
|
@@ -228,7 +319,8 @@ export class TXEOracleTopLevelContext {
|
|
|
228
319
|
globals.version = this.version;
|
|
229
320
|
globals.gasFees = GasFees.empty();
|
|
230
321
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
231
|
-
const
|
|
322
|
+
const bindings = this.logger.getBindings();
|
|
323
|
+
const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore), bindings);
|
|
232
324
|
const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
|
|
233
325
|
const config = PublicSimulatorConfig.from({
|
|
234
326
|
skipFeeEnforcement: true,
|
|
@@ -237,7 +329,7 @@ export class TXEOracleTopLevelContext {
|
|
|
237
329
|
collectStatistics: false,
|
|
238
330
|
collectCallMetadata: true
|
|
239
331
|
});
|
|
240
|
-
const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, new
|
|
332
|
+
const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings), new TestDateProvider(), undefined, createLogger('simulator:public-processor', bindings));
|
|
241
333
|
const tx = await Tx.create({
|
|
242
334
|
data: publicInputs,
|
|
243
335
|
chonkProof: ChonkProof.empty(),
|
|
@@ -258,7 +350,7 @@ export class TXEOracleTopLevelContext {
|
|
|
258
350
|
} else if (!processedTx.revertCode.isOK()) {
|
|
259
351
|
if (processedTx.revertReason) {
|
|
260
352
|
try {
|
|
261
|
-
await enrichPublicSimulationError(processedTx.revertReason, this.
|
|
353
|
+
await enrichPublicSimulationError(processedTx.revertReason, this.contractStore, this.logger);
|
|
262
354
|
// eslint-disable-next-line no-empty
|
|
263
355
|
} catch {}
|
|
264
356
|
throw new Error(`Contract execution has reverted: ${processedTx.revertReason.getMessage()}`);
|
|
@@ -266,10 +358,20 @@ export class TXEOracleTopLevelContext {
|
|
|
266
358
|
throw new Error('Contract execution has reverted');
|
|
267
359
|
}
|
|
268
360
|
}
|
|
361
|
+
// Walk the nested private-call tree and collect every offchain effect the transaction emitted.
|
|
362
|
+
// PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
|
|
363
|
+
// `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
|
|
364
|
+
// we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
|
|
365
|
+
const offchainEffects = collectNested([
|
|
366
|
+
executionResult
|
|
367
|
+
], (r)=>r.offchainEffects.map((e)=>e.data));
|
|
269
368
|
if (isStaticCall) {
|
|
270
369
|
await checkpoint.revert();
|
|
271
370
|
await forkedWorldTrees.close();
|
|
272
|
-
return
|
|
371
|
+
return {
|
|
372
|
+
returnValues: executionResult.returnValues ?? [],
|
|
373
|
+
offchainEffects
|
|
374
|
+
};
|
|
273
375
|
}
|
|
274
376
|
const txEffect = TxEffect.empty();
|
|
275
377
|
txEffect.noteHashes = processedTx.txEffect.noteHashes;
|
|
@@ -280,22 +382,24 @@ export class TXEOracleTopLevelContext {
|
|
|
280
382
|
txEffect.txHash = new TxHash(new Fr(blockNumber));
|
|
281
383
|
const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
|
|
282
384
|
await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
|
|
283
|
-
const
|
|
385
|
+
const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [
|
|
284
386
|
txEffect
|
|
285
387
|
]);
|
|
286
|
-
const l2Block = new L2Block(makeAppendOnlyTreeSnapshot(), await makeTXEBlockHeader(forkedWorldTrees, globals), body);
|
|
287
388
|
await this.stateMachine.handleL2Block(l2Block);
|
|
288
389
|
await forkedWorldTrees.close();
|
|
289
|
-
return
|
|
390
|
+
return {
|
|
391
|
+
returnValues: executionResult.returnValues ?? [],
|
|
392
|
+
offchainEffects
|
|
393
|
+
};
|
|
290
394
|
}
|
|
291
|
-
async
|
|
292
|
-
this.logger.verbose(`Executing public function ${await this.
|
|
293
|
-
const blockNumber = await this.
|
|
294
|
-
const gasLimits = new Gas(
|
|
295
|
-
const teardownGasLimits = new Gas(
|
|
395
|
+
async publicCallNewFlow(from, targetContractAddress, calldata, isStaticCall) {
|
|
396
|
+
this.logger.verbose(`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`);
|
|
397
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
398
|
+
const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
|
|
399
|
+
const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
|
|
296
400
|
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
297
401
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
298
|
-
const anchorBlockHeader = await this.
|
|
402
|
+
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
299
403
|
const calldataHash = await computeCalldataHash(calldata);
|
|
300
404
|
const calldataHashedValues = new HashedValues(calldata, calldataHash);
|
|
301
405
|
const globals = makeGlobalVariables();
|
|
@@ -305,7 +409,8 @@ export class TXEOracleTopLevelContext {
|
|
|
305
409
|
globals.version = this.version;
|
|
306
410
|
globals.gasFees = GasFees.empty();
|
|
307
411
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
308
|
-
const
|
|
412
|
+
const bindings2 = this.logger.getBindings();
|
|
413
|
+
const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore), bindings2);
|
|
309
414
|
const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
|
|
310
415
|
const config = PublicSimulatorConfig.from({
|
|
311
416
|
skipFeeEnforcement: true,
|
|
@@ -314,23 +419,21 @@ export class TXEOracleTopLevelContext {
|
|
|
314
419
|
collectStatistics: false,
|
|
315
420
|
collectCallMetadata: true
|
|
316
421
|
});
|
|
317
|
-
const simulator = new
|
|
318
|
-
const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, simulator, new TestDateProvider());
|
|
422
|
+
const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings2);
|
|
423
|
+
const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, simulator, new TestDateProvider(), undefined, createLogger('simulator:public-processor', bindings2));
|
|
319
424
|
// We're simulating a scenario in which private execution immediately enqueues a public call and halts. The private
|
|
320
425
|
// kernel init would in this case inject a nullifier with the transaction request hash as a non-revertible
|
|
321
426
|
// side-effect, which the AVM then expects to exist in order to use it as the nonce generator when siloing notes as
|
|
322
427
|
// unique.
|
|
323
428
|
const nonRevertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
324
|
-
|
|
325
|
-
nonRevertibleAccumulatedData.nullifiers[0] = getSingleTxBlockRequestHash(blockNumber);
|
|
326
|
-
}
|
|
429
|
+
nonRevertibleAccumulatedData.nullifiers[0] = getSingleTxBlockRequestHash(blockNumber);
|
|
327
430
|
// The enqueued public call itself we make be revertible so that the public execution is itself revertible, as tests
|
|
328
431
|
// may require producing reverts.
|
|
329
432
|
const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
330
433
|
revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(from, targetContractAddress, isStaticCall, calldataHash);
|
|
331
434
|
const inputsForPublic = new PartialPrivateTailPublicInputsForPublic(nonRevertibleAccumulatedData, revertibleAccumulatedData, PublicCallRequest.empty());
|
|
332
435
|
const constantData = new TxConstantData(anchorBlockHeader, txContext, Fr.zero(), Fr.zero());
|
|
333
|
-
const txData = new PrivateKernelTailCircuitPublicInputs(constantData, /*gasUsed=*/ new Gas(0, 0), /*feePayer=*/ AztecAddress.zero(), /*
|
|
436
|
+
const txData = new PrivateKernelTailCircuitPublicInputs(constantData, /*gasUsed=*/ new Gas(0, 0), /*feePayer=*/ AztecAddress.zero(), /*expirationTimestamp=*/ 0n, inputsForPublic, undefined);
|
|
334
437
|
const tx = await Tx.create({
|
|
335
438
|
data: txData,
|
|
336
439
|
chonkProof: ChonkProof.empty(),
|
|
@@ -353,7 +456,7 @@ export class TXEOracleTopLevelContext {
|
|
|
353
456
|
} else if (!processedTx.revertCode.isOK()) {
|
|
354
457
|
if (processedTx.revertReason) {
|
|
355
458
|
try {
|
|
356
|
-
await enrichPublicSimulationError(processedTx.revertReason, this.
|
|
459
|
+
await enrichPublicSimulationError(processedTx.revertReason, this.contractStore, this.logger);
|
|
357
460
|
// eslint-disable-next-line no-empty
|
|
358
461
|
} catch {}
|
|
359
462
|
throw new Error(`Contract execution has reverted: ${processedTx.revertReason.getMessage()}`);
|
|
@@ -376,25 +479,37 @@ export class TXEOracleTopLevelContext {
|
|
|
376
479
|
txEffect.txHash = new TxHash(new Fr(blockNumber));
|
|
377
480
|
const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
|
|
378
481
|
await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
|
|
379
|
-
const
|
|
482
|
+
const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [
|
|
380
483
|
txEffect
|
|
381
484
|
]);
|
|
382
|
-
const l2Block = new L2Block(makeAppendOnlyTreeSnapshot(), await makeTXEBlockHeader(forkedWorldTrees, globals), body);
|
|
383
485
|
await this.stateMachine.handleL2Block(l2Block);
|
|
384
486
|
await forkedWorldTrees.close();
|
|
385
487
|
return returnValues ?? [];
|
|
386
488
|
}
|
|
387
|
-
async
|
|
388
|
-
const artifact = await this.
|
|
489
|
+
async executeUtilityFunction(targetContractAddress, functionSelector, args, jobId) {
|
|
490
|
+
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
389
491
|
if (!artifact) {
|
|
390
492
|
throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
|
|
391
493
|
}
|
|
392
|
-
|
|
494
|
+
// Sync notes before executing utility function to discover notes from previous transactions
|
|
495
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
496
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(targetContractAddress, functionSelector, async (call, execScopes)=>{
|
|
497
|
+
await this.executeUtilityCall(call, execScopes, jobId);
|
|
498
|
+
}, blockHeader, jobId, await this.keyStore.getAccounts());
|
|
499
|
+
const call = FunctionCall.from({
|
|
393
500
|
name: artifact.name,
|
|
501
|
+
to: targetContractAddress,
|
|
394
502
|
selector: functionSelector,
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
503
|
+
type: FunctionType.UTILITY,
|
|
504
|
+
hideMsgSender: false,
|
|
505
|
+
isStatic: false,
|
|
506
|
+
args,
|
|
507
|
+
returnTypes: []
|
|
508
|
+
});
|
|
509
|
+
return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId);
|
|
510
|
+
}
|
|
511
|
+
async executeUtilityCall(call, scopes, jobId) {
|
|
512
|
+
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
398
513
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
399
514
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
400
515
|
}
|
|
@@ -403,8 +518,28 @@ export class TXEOracleTopLevelContext {
|
|
|
403
518
|
selector: call.selector
|
|
404
519
|
});
|
|
405
520
|
try {
|
|
406
|
-
const
|
|
407
|
-
const
|
|
521
|
+
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
522
|
+
const oracle = new UtilityExecutionOracle({
|
|
523
|
+
contractAddress: call.to,
|
|
524
|
+
authWitnesses: [],
|
|
525
|
+
capsules: [],
|
|
526
|
+
anchorBlockHeader,
|
|
527
|
+
contractStore: this.contractStore,
|
|
528
|
+
noteStore: this.noteStore,
|
|
529
|
+
keyStore: this.keyStore,
|
|
530
|
+
addressStore: this.addressStore,
|
|
531
|
+
aztecNode: this.stateMachine.node,
|
|
532
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
533
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
534
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
535
|
+
privateEventStore: this.privateEventStore,
|
|
536
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
537
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
538
|
+
l2TipsStore: this.stateMachine.node,
|
|
539
|
+
jobId,
|
|
540
|
+
scopes
|
|
541
|
+
});
|
|
542
|
+
const acirExecutionResult = await new WASMSimulator().executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback()).catch((err)=>{
|
|
408
543
|
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
409
544
|
throw new ExecutionError(err.message, {
|
|
410
545
|
contractAddress: call.to,
|
|
@@ -413,10 +548,10 @@ export class TXEOracleTopLevelContext {
|
|
|
413
548
|
cause: err
|
|
414
549
|
});
|
|
415
550
|
});
|
|
416
|
-
this.logger.verbose(`Utility
|
|
551
|
+
this.logger.verbose(`Utility execution for ${call.to}.${call.selector} completed`);
|
|
417
552
|
return witnessMapToFields(acirExecutionResult.returnWitness);
|
|
418
553
|
} catch (err) {
|
|
419
|
-
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility
|
|
554
|
+
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility execution'));
|
|
420
555
|
}
|
|
421
556
|
}
|
|
422
557
|
close() {
|
|
@@ -427,6 +562,7 @@ export class TXEOracleTopLevelContext {
|
|
|
427
562
|
];
|
|
428
563
|
}
|
|
429
564
|
async getLastBlockNumber() {
|
|
430
|
-
|
|
565
|
+
const header = await this.stateMachine.node.getBlockHeader('latest');
|
|
566
|
+
return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
|
|
431
567
|
}
|
|
432
568
|
}
|