@aztec/txe 0.0.1-commit.fcb71a6 → 0.0.1-commit.ff7989d6c
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/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +82 -50
- package/dest/oracle/interfaces.d.ts +4 -4
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.d.ts +3 -3
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +6 -6
- package/dest/oracle/txe_oracle_top_level_context.d.ts +6 -6
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +107 -40
- package/dest/rpc_translator.d.ts +21 -15
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +78 -53
- package/dest/state_machine/archiver.d.ts +20 -67
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +59 -178
- package/dest/state_machine/dummy_p2p_client.d.ts +19 -14
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +38 -23
- package/dest/state_machine/global_variable_builder.d.ts +2 -2
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
- package/dest/state_machine/global_variable_builder.js +1 -1
- package/dest/state_machine/index.d.ts +5 -5
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +35 -12
- package/dest/state_machine/mock_epoch_cache.d.ts +9 -6
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +14 -7
- package/dest/state_machine/synchronizer.d.ts +3 -3
- package/dest/state_machine/synchronizer.d.ts.map +1 -1
- package/dest/txe_session.d.ts +6 -6
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +92 -24
- package/dest/util/encoding.d.ts +17 -17
- package/dest/util/txe_public_contract_data_source.d.ts +2 -3
- package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
- package/dest/util/txe_public_contract_data_source.js +5 -22
- package/dest/utils/block_creation.d.ts +4 -4
- package/dest/utils/block_creation.d.ts.map +1 -1
- package/dest/utils/block_creation.js +18 -5
- package/dest/utils/tx_effect_creation.d.ts +2 -3
- package/dest/utils/tx_effect_creation.d.ts.map +1 -1
- package/dest/utils/tx_effect_creation.js +3 -6
- package/package.json +16 -16
- package/src/index.ts +83 -49
- package/src/oracle/interfaces.ts +3 -3
- package/src/oracle/txe_oracle_public_context.ts +6 -8
- package/src/oracle/txe_oracle_top_level_context.ts +131 -91
- package/src/rpc_translator.ts +81 -55
- package/src/state_machine/archiver.ts +54 -220
- package/src/state_machine/dummy_p2p_client.ts +54 -31
- package/src/state_machine/global_variable_builder.ts +1 -1
- package/src/state_machine/index.ts +49 -11
- package/src/state_machine/mock_epoch_cache.ts +15 -11
- package/src/state_machine/synchronizer.ts +2 -2
- package/src/txe_session.ts +99 -80
- package/src/util/txe_public_contract_data_source.ts +10 -36
- package/src/utils/block_creation.ts +19 -16
- package/src/utils/tx_effect_creation.ts +3 -11
- package/dest/util/txe_contract_store.d.ts +0 -12
- package/dest/util/txe_contract_store.d.ts.map +0 -1
- package/dest/util/txe_contract_store.js +0 -22
- package/src/util/txe_contract_store.ts +0 -36
package/src/index.ts
CHANGED
|
@@ -9,9 +9,12 @@ import { Fr } from '@aztec/aztec.js/fields';
|
|
|
9
9
|
import { PublicKeys, deriveKeys } from '@aztec/aztec.js/keys';
|
|
10
10
|
import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
|
|
11
11
|
import type { Logger } from '@aztec/foundation/log';
|
|
12
|
-
import {
|
|
12
|
+
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
|
|
13
|
+
import { protocolContractNames } from '@aztec/protocol-contracts';
|
|
13
14
|
import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
|
|
15
|
+
import { ContractStore } from '@aztec/pxe/server';
|
|
14
16
|
import { computeArtifactHash } from '@aztec/stdlib/contract';
|
|
17
|
+
import type { ContractArtifactWithHash } from '@aztec/stdlib/contract';
|
|
15
18
|
import type { ApiSchemaFor } from '@aztec/stdlib/schemas';
|
|
16
19
|
import { zodFor } from '@aztec/stdlib/schemas';
|
|
17
20
|
|
|
@@ -33,18 +36,24 @@ import {
|
|
|
33
36
|
fromSingle,
|
|
34
37
|
toSingle,
|
|
35
38
|
} from './util/encoding.js';
|
|
36
|
-
import type { ContractArtifactWithHash } from './util/txe_contract_store.js';
|
|
37
39
|
|
|
38
40
|
const sessions = new Map<number, TXESession>();
|
|
39
41
|
|
|
40
42
|
/*
|
|
41
43
|
* TXE typically has to load the same contract artifacts over and over again for multiple tests,
|
|
42
|
-
* so we cache them here to avoid
|
|
44
|
+
* so we cache them here to avoid loading from disk repeatedly.
|
|
45
|
+
*
|
|
46
|
+
* The in-flight map coalesces concurrent requests for the same cache key so that
|
|
47
|
+
* computeArtifactHash (very expensive) is only run once even under parallelism.
|
|
43
48
|
*/
|
|
44
49
|
const TXEArtifactsCache = new Map<
|
|
45
50
|
string,
|
|
46
51
|
{ artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }
|
|
47
52
|
>();
|
|
53
|
+
const TXEArtifactsCacheInFlight = new Map<
|
|
54
|
+
string,
|
|
55
|
+
Promise<{ artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }>
|
|
56
|
+
>();
|
|
48
57
|
|
|
49
58
|
type TXEForeignCallInput = {
|
|
50
59
|
session_id: number;
|
|
@@ -68,7 +77,7 @@ const TXEForeignCallInputSchema = zodFor<TXEForeignCallInput>()(
|
|
|
68
77
|
);
|
|
69
78
|
|
|
70
79
|
class TXEDispatcher {
|
|
71
|
-
private
|
|
80
|
+
private contractStore!: ContractStore;
|
|
72
81
|
|
|
73
82
|
constructor(private logger: Logger) {}
|
|
74
83
|
|
|
@@ -135,29 +144,36 @@ class TXEDispatcher {
|
|
|
135
144
|
this.logger.debug(`Using cached artifact for ${cacheKey}`);
|
|
136
145
|
({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
|
|
137
146
|
} else {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
147
|
+
if (!TXEArtifactsCacheInFlight.has(cacheKey)) {
|
|
148
|
+
this.logger.debug(`Loading compiled artifact ${artifactPath}`);
|
|
149
|
+
const compute = async () => {
|
|
150
|
+
const artifactJSON = JSON.parse(await readFile(artifactPath, 'utf-8')) as NoirCompiledContract;
|
|
151
|
+
const artifactWithoutHash = loadContractArtifact(artifactJSON);
|
|
152
|
+
const computedArtifact: ContractArtifactWithHash = {
|
|
153
|
+
...artifactWithoutHash,
|
|
154
|
+
// Artifact hash is *very* expensive to compute, so we do it here once
|
|
155
|
+
// and the TXE contract data provider can cache it
|
|
156
|
+
artifactHash: await computeArtifactHash(artifactWithoutHash),
|
|
157
|
+
};
|
|
158
|
+
this.logger.debug(
|
|
159
|
+
`Deploy ${computedArtifact.name} with initializer ${initializer}(${decodedArgs}) and public keys hash ${publicKeysHash.toString()}`,
|
|
160
|
+
);
|
|
161
|
+
const computedInstance = await getContractInstanceFromInstantiationParams(computedArtifact, {
|
|
162
|
+
constructorArgs: decodedArgs,
|
|
163
|
+
skipArgsDecoding: true,
|
|
164
|
+
salt: Fr.ONE,
|
|
165
|
+
publicKeys,
|
|
166
|
+
constructorArtifact: initializer ? initializer : undefined,
|
|
167
|
+
deployer: AztecAddress.ZERO,
|
|
168
|
+
});
|
|
169
|
+
const result = { artifact: computedArtifact, instance: computedInstance };
|
|
170
|
+
TXEArtifactsCache.set(cacheKey, result);
|
|
171
|
+
TXEArtifactsCacheInFlight.delete(cacheKey);
|
|
172
|
+
return result;
|
|
173
|
+
};
|
|
174
|
+
TXEArtifactsCacheInFlight.set(cacheKey, compute());
|
|
175
|
+
}
|
|
176
|
+
({ artifact, instance } = await TXEArtifactsCacheInFlight.get(cacheKey)!);
|
|
161
177
|
}
|
|
162
178
|
|
|
163
179
|
inputs.splice(0, 1, artifact, instance, toSingle(secret));
|
|
@@ -175,23 +191,35 @@ class TXEDispatcher {
|
|
|
175
191
|
this.logger.debug(`Using cached artifact for ${cacheKey}`);
|
|
176
192
|
({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
|
|
177
193
|
} else {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
if (!TXEArtifactsCacheInFlight.has(cacheKey)) {
|
|
195
|
+
const compute = async () => {
|
|
196
|
+
const keys = await deriveKeys(secret);
|
|
197
|
+
const args = [
|
|
198
|
+
keys.publicKeys.masterIncomingViewingPublicKey.x,
|
|
199
|
+
keys.publicKeys.masterIncomingViewingPublicKey.y,
|
|
200
|
+
];
|
|
201
|
+
const computedArtifact: ContractArtifactWithHash = {
|
|
202
|
+
...SchnorrAccountContractArtifact,
|
|
203
|
+
// Artifact hash is *very* expensive to compute, so we do it here once
|
|
204
|
+
// and the TXE contract data provider can cache it
|
|
205
|
+
artifactHash: await computeArtifactHash(SchnorrAccountContractArtifact),
|
|
206
|
+
};
|
|
207
|
+
const computedInstance = await getContractInstanceFromInstantiationParams(computedArtifact, {
|
|
208
|
+
constructorArgs: args,
|
|
209
|
+
skipArgsDecoding: true,
|
|
210
|
+
salt: Fr.ONE,
|
|
211
|
+
publicKeys: keys.publicKeys,
|
|
212
|
+
constructorArtifact: 'constructor',
|
|
213
|
+
deployer: AztecAddress.ZERO,
|
|
214
|
+
});
|
|
215
|
+
const result = { artifact: computedArtifact, instance: computedInstance };
|
|
216
|
+
TXEArtifactsCache.set(cacheKey, result);
|
|
217
|
+
TXEArtifactsCacheInFlight.delete(cacheKey);
|
|
218
|
+
return result;
|
|
219
|
+
};
|
|
220
|
+
TXEArtifactsCacheInFlight.set(cacheKey, compute());
|
|
221
|
+
}
|
|
222
|
+
({ artifact, instance } = await TXEArtifactsCacheInFlight.get(cacheKey)!);
|
|
195
223
|
}
|
|
196
224
|
|
|
197
225
|
inputs.splice(0, 0, artifact, instance);
|
|
@@ -204,12 +232,18 @@ class TXEDispatcher {
|
|
|
204
232
|
|
|
205
233
|
if (!sessions.has(sessionId)) {
|
|
206
234
|
this.logger.debug(`Creating new session ${sessionId}`);
|
|
207
|
-
if (!this.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
);
|
|
235
|
+
if (!this.contractStore) {
|
|
236
|
+
const kvStore = await openTmpStore('txe-contracts');
|
|
237
|
+
this.contractStore = new ContractStore(kvStore);
|
|
238
|
+
const provider = new BundledProtocolContractsProvider();
|
|
239
|
+
for (const name of protocolContractNames) {
|
|
240
|
+
const { instance, artifact } = await provider.getProtocolContractArtifact(name);
|
|
241
|
+
await this.contractStore.addContractArtifact(artifact);
|
|
242
|
+
await this.contractStore.addContractInstance(instance);
|
|
243
|
+
}
|
|
244
|
+
this.logger.debug('Registered protocol contracts in shared contract store');
|
|
211
245
|
}
|
|
212
|
-
sessions.set(sessionId, await TXESession.init(this.
|
|
246
|
+
sessions.set(sessionId, await TXESession.init(this.contractStore));
|
|
213
247
|
}
|
|
214
248
|
|
|
215
249
|
switch (functionName) {
|
package/src/oracle/interfaces.ts
CHANGED
|
@@ -33,9 +33,9 @@ export interface IAvmExecutionOracle {
|
|
|
33
33
|
avmOpcodeVersion(): Promise<Fr>;
|
|
34
34
|
avmOpcodeEmitNullifier(nullifier: Fr): Promise<void>;
|
|
35
35
|
avmOpcodeEmitNoteHash(noteHash: Fr): Promise<void>;
|
|
36
|
-
avmOpcodeNullifierExists(
|
|
36
|
+
avmOpcodeNullifierExists(siloedNullifier: Fr): Promise<boolean>;
|
|
37
37
|
avmOpcodeStorageWrite(slot: Fr, value: Fr): Promise<void>;
|
|
38
|
-
avmOpcodeStorageRead(slot: Fr): Promise<Fr>;
|
|
38
|
+
avmOpcodeStorageRead(slot: Fr, contractAddress: AztecAddress): Promise<Fr>;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -72,7 +72,7 @@ export interface ITxeExecutionOracle {
|
|
|
72
72
|
argsHash: Fr,
|
|
73
73
|
isStaticCall: boolean,
|
|
74
74
|
): Promise<Fr[]>;
|
|
75
|
-
|
|
75
|
+
txeExecuteUtilityFunction(
|
|
76
76
|
targetContractAddress: AztecAddress,
|
|
77
77
|
functionSelector: FunctionSelector,
|
|
78
78
|
args: Fr[],
|
|
@@ -78,13 +78,11 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle {
|
|
|
78
78
|
this.transientUniqueNoteHashes.push(siloedNoteHash);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
async avmOpcodeNullifierExists(
|
|
82
|
-
const nullifier = await siloNullifier(targetAddress, innerNullifier!);
|
|
83
|
-
|
|
81
|
+
async avmOpcodeNullifierExists(siloedNullifier: Fr): Promise<boolean> {
|
|
84
82
|
const treeIndex = (
|
|
85
|
-
await this.forkedWorldTrees.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [
|
|
83
|
+
await this.forkedWorldTrees.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()])
|
|
86
84
|
)[0];
|
|
87
|
-
const transientIndex = this.transientSiloedNullifiers.find(n => n.equals(
|
|
85
|
+
const transientIndex = this.transientSiloedNullifiers.find(n => n.equals(siloedNullifier));
|
|
88
86
|
|
|
89
87
|
return treeIndex !== undefined || transientIndex !== undefined;
|
|
90
88
|
}
|
|
@@ -101,8 +99,8 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle {
|
|
|
101
99
|
]);
|
|
102
100
|
}
|
|
103
101
|
|
|
104
|
-
async avmOpcodeStorageRead(slot: Fr): Promise<Fr> {
|
|
105
|
-
const leafSlot = await computePublicDataTreeLeafSlot(
|
|
102
|
+
async avmOpcodeStorageRead(slot: Fr, contractAddress: AztecAddress): Promise<Fr> {
|
|
103
|
+
const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot);
|
|
106
104
|
|
|
107
105
|
const lowLeafResult = await this.forkedWorldTrees.getPreviousValueIndex(
|
|
108
106
|
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
@@ -119,7 +117,7 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle {
|
|
|
119
117
|
)) as PublicDataTreeLeafPreimage
|
|
120
118
|
).leaf.value;
|
|
121
119
|
|
|
122
|
-
this.logger.debug('AVM storage read', { slot, value });
|
|
120
|
+
this.logger.debug('AVM storage read', { slot, contractAddress, value });
|
|
123
121
|
|
|
124
122
|
return value;
|
|
125
123
|
}
|
|
@@ -12,9 +12,11 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
12
12
|
import { LogLevels, type Logger, applyStringFormatting, createLogger } from '@aztec/foundation/log';
|
|
13
13
|
import { TestDateProvider } from '@aztec/foundation/timer';
|
|
14
14
|
import type { KeyStore } from '@aztec/key-store';
|
|
15
|
+
import type { AccessScopes } from '@aztec/pxe/client/lazy';
|
|
15
16
|
import {
|
|
16
17
|
AddressStore,
|
|
17
18
|
CapsuleStore,
|
|
19
|
+
type ContractStore,
|
|
18
20
|
NoteStore,
|
|
19
21
|
ORACLE_VERSION,
|
|
20
22
|
PrivateEventStore,
|
|
@@ -83,7 +85,6 @@ import { ForkCheckpoint } from '@aztec/world-state';
|
|
|
83
85
|
import { DEFAULT_ADDRESS } from '../constants.js';
|
|
84
86
|
import type { TXEStateMachine } from '../state_machine/index.js';
|
|
85
87
|
import type { TXEAccountStore } from '../util/txe_account_store.js';
|
|
86
|
-
import type { TXEContractStore } from '../util/txe_contract_store.js';
|
|
87
88
|
import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
|
|
88
89
|
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
|
|
89
90
|
import type { ITxeExecutionOracle } from './interfaces.js';
|
|
@@ -96,7 +97,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
96
97
|
|
|
97
98
|
constructor(
|
|
98
99
|
private stateMachine: TXEStateMachine,
|
|
99
|
-
private contractStore:
|
|
100
|
+
private contractStore: ContractStore,
|
|
100
101
|
private noteStore: NoteStore,
|
|
101
102
|
private keyStore: KeyStore,
|
|
102
103
|
private addressStore: AddressStore,
|
|
@@ -106,6 +107,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
106
107
|
private senderAddressBookStore: SenderAddressBookStore,
|
|
107
108
|
private capsuleStore: CapsuleStore,
|
|
108
109
|
private privateEventStore: PrivateEventStore,
|
|
110
|
+
private jobId: string,
|
|
109
111
|
private nextBlockTimestamp: bigint,
|
|
110
112
|
private version: Fr,
|
|
111
113
|
private chainId: Fr,
|
|
@@ -130,13 +132,14 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
// We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
|
|
133
|
-
|
|
135
|
+
utilityLog(level: number, message: string, fields: Fr[]): Promise<void> {
|
|
134
136
|
if (!LogLevels[level]) {
|
|
135
|
-
throw new Error(`Invalid
|
|
137
|
+
throw new Error(`Invalid log level: ${level}`);
|
|
136
138
|
}
|
|
137
139
|
const levelName = LogLevels[level];
|
|
138
140
|
|
|
139
141
|
this.logger[levelName](`${applyStringFormatting(message, fields)}`, { module: `${this.logger.module}:debug_log` });
|
|
142
|
+
return Promise.resolve();
|
|
140
143
|
}
|
|
141
144
|
|
|
142
145
|
txeGetDefaultAddress(): AztecAddress {
|
|
@@ -156,7 +159,8 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
async txeGetLastTxEffects() {
|
|
159
|
-
const
|
|
162
|
+
const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
|
|
163
|
+
const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
|
|
160
164
|
|
|
161
165
|
if (block!.body.txEffects.length != 1) {
|
|
162
166
|
// Note that calls like env.mine() will result in blocks with no transactions, hitting this
|
|
@@ -207,7 +211,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
207
211
|
await this.txeAddAccount(artifact, instance, secret);
|
|
208
212
|
} else {
|
|
209
213
|
await this.contractStore.addContractInstance(instance);
|
|
210
|
-
await this.contractStore.addContractArtifact(
|
|
214
|
+
await this.contractStore.addContractArtifact(artifact);
|
|
211
215
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
212
216
|
}
|
|
213
217
|
}
|
|
@@ -217,7 +221,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
217
221
|
|
|
218
222
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
219
223
|
await this.contractStore.addContractInstance(instance);
|
|
220
|
-
await this.contractStore.addContractArtifact(
|
|
224
|
+
await this.contractStore.addContractArtifact(artifact);
|
|
221
225
|
|
|
222
226
|
const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
|
|
223
227
|
await this.accountStore.setAccount(completeAddress.address, completeAddress);
|
|
@@ -294,12 +298,24 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
294
298
|
throw new Error(message);
|
|
295
299
|
}
|
|
296
300
|
|
|
301
|
+
// When `from` is the zero address (e.g. when deploying a new account contract), we return an
|
|
302
|
+
// empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
|
|
303
|
+
const effectiveScopes = from.isZero() ? [] : [from];
|
|
304
|
+
|
|
297
305
|
// Sync notes before executing private function to discover notes from previous transactions
|
|
298
|
-
const utilityExecutor = async (call: FunctionCall) => {
|
|
299
|
-
await this.executeUtilityCall(call);
|
|
306
|
+
const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
|
|
307
|
+
await this.executeUtilityCall(call, execScopes);
|
|
300
308
|
};
|
|
301
309
|
|
|
302
|
-
await this.
|
|
310
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
311
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(
|
|
312
|
+
targetContractAddress,
|
|
313
|
+
functionSelector,
|
|
314
|
+
utilityExecutor,
|
|
315
|
+
blockHeader,
|
|
316
|
+
this.jobId,
|
|
317
|
+
effectiveScopes,
|
|
318
|
+
);
|
|
303
319
|
|
|
304
320
|
const blockNumber = await this.txeGetNextBlockNumber();
|
|
305
321
|
|
|
@@ -311,50 +327,48 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
311
327
|
|
|
312
328
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
313
329
|
|
|
314
|
-
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
315
|
-
|
|
316
330
|
const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
|
|
317
331
|
const noteCache = new ExecutionNoteCache(protocolNullifier);
|
|
332
|
+
// In production, the account contract sets the min revertible counter before calling the app function.
|
|
333
|
+
// Since TXE bypasses the account contract, we simulate this by setting minRevertibleSideEffectCounter to 1,
|
|
334
|
+
// marking all side effects as revertible.
|
|
335
|
+
const minRevertibleSideEffectCounter = 1;
|
|
336
|
+
await noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
|
|
318
337
|
const taggingIndexCache = new ExecutionTaggingIndexCache();
|
|
319
338
|
|
|
320
339
|
const simulator = new WASMSimulator();
|
|
321
340
|
|
|
322
|
-
const privateExecutionOracle = new PrivateExecutionOracle(
|
|
341
|
+
const privateExecutionOracle = new PrivateExecutionOracle({
|
|
323
342
|
argsHash,
|
|
324
343
|
txContext,
|
|
325
344
|
callContext,
|
|
326
|
-
|
|
327
|
-
blockHeader,
|
|
345
|
+
anchorBlockHeader: blockHeader,
|
|
328
346
|
utilityExecutor,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
[],
|
|
333
|
-
HashedValuesCache.create([new HashedValues(args, argsHash)]),
|
|
347
|
+
authWitnesses: Array.from(this.authwits.values()),
|
|
348
|
+
capsules: [],
|
|
349
|
+
executionCache: HashedValuesCache.create([new HashedValues(args, argsHash)]),
|
|
334
350
|
noteCache,
|
|
335
351
|
taggingIndexCache,
|
|
336
|
-
this.contractStore,
|
|
337
|
-
this.noteStore,
|
|
338
|
-
this.keyStore,
|
|
339
|
-
this.addressStore,
|
|
340
|
-
this.stateMachine.node,
|
|
341
|
-
this.
|
|
342
|
-
this.
|
|
343
|
-
this.
|
|
344
|
-
this.
|
|
345
|
-
this.
|
|
346
|
-
this.
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
*/
|
|
355
|
-
from,
|
|
352
|
+
contractStore: this.contractStore,
|
|
353
|
+
noteStore: this.noteStore,
|
|
354
|
+
keyStore: this.keyStore,
|
|
355
|
+
addressStore: this.addressStore,
|
|
356
|
+
aztecNode: this.stateMachine.node,
|
|
357
|
+
senderTaggingStore: this.senderTaggingStore,
|
|
358
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
359
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
360
|
+
capsuleStore: this.capsuleStore,
|
|
361
|
+
privateEventStore: this.privateEventStore,
|
|
362
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
363
|
+
jobId: this.jobId,
|
|
364
|
+
totalPublicCalldataCount: 0,
|
|
365
|
+
sideEffectCounter: minRevertibleSideEffectCounter,
|
|
366
|
+
scopes: effectiveScopes,
|
|
367
|
+
// In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
368
|
+
// contract would perform, including setting senderForTags.
|
|
369
|
+
senderForTags: from,
|
|
356
370
|
simulator,
|
|
357
|
-
);
|
|
371
|
+
});
|
|
358
372
|
|
|
359
373
|
// 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.
|
|
360
374
|
let result: PrivateExecutionResult;
|
|
@@ -381,19 +395,22 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
381
395
|
}),
|
|
382
396
|
);
|
|
383
397
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
result = new PrivateExecutionResult(executionResult, Fr.ZERO, publicFunctionsCalldata);
|
|
398
|
+
noteCache.finish();
|
|
399
|
+
const nonceGenerator = noteCache.getNonceGenerator();
|
|
400
|
+
result = new PrivateExecutionResult(executionResult, nonceGenerator, publicFunctionsCalldata);
|
|
388
401
|
} catch (err) {
|
|
389
402
|
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
390
403
|
}
|
|
391
404
|
|
|
392
|
-
// According to the protocol rules,
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
const
|
|
396
|
-
|
|
405
|
+
// According to the protocol rules, there must be at least one nullifier in the tx. The first nullifier is used as
|
|
406
|
+
// the nonce generator for the note hashes.
|
|
407
|
+
// We pass the non-zero minRevertibleSideEffectCounter to make sure the side effects are split correctly.
|
|
408
|
+
const { publicInputs } = await generateSimulatedProvingResult(
|
|
409
|
+
result,
|
|
410
|
+
(addr, sel) => this.contractStore.getDebugFunctionName(addr, sel),
|
|
411
|
+
this.stateMachine.node,
|
|
412
|
+
minRevertibleSideEffectCounter,
|
|
413
|
+
);
|
|
397
414
|
|
|
398
415
|
const globals = makeGlobalVariables();
|
|
399
416
|
globals.blockNumber = blockNumber;
|
|
@@ -404,7 +421,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
404
421
|
|
|
405
422
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
406
423
|
|
|
407
|
-
const
|
|
424
|
+
const bindings = this.logger.getBindings();
|
|
425
|
+
const contractsDB = new PublicContractsDB(
|
|
426
|
+
new TXEPublicContractDataSource(blockNumber, this.contractStore),
|
|
427
|
+
bindings,
|
|
428
|
+
);
|
|
408
429
|
const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
|
|
409
430
|
const config = PublicSimulatorConfig.from({
|
|
410
431
|
skipFeeEnforcement: true,
|
|
@@ -417,8 +438,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
417
438
|
globals,
|
|
418
439
|
guardedMerkleTrees,
|
|
419
440
|
contractsDB,
|
|
420
|
-
new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config),
|
|
441
|
+
new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings),
|
|
421
442
|
new TestDateProvider(),
|
|
443
|
+
undefined,
|
|
444
|
+
createLogger('simulator:public-processor', bindings),
|
|
422
445
|
);
|
|
423
446
|
|
|
424
447
|
const tx = await Tx.create({
|
|
@@ -515,7 +538,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
515
538
|
|
|
516
539
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
517
540
|
|
|
518
|
-
const
|
|
541
|
+
const bindings2 = this.logger.getBindings();
|
|
542
|
+
const contractsDB = new PublicContractsDB(
|
|
543
|
+
new TXEPublicContractDataSource(blockNumber, this.contractStore),
|
|
544
|
+
bindings2,
|
|
545
|
+
);
|
|
519
546
|
const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
|
|
520
547
|
const config = PublicSimulatorConfig.from({
|
|
521
548
|
skipFeeEnforcement: true,
|
|
@@ -524,8 +551,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
524
551
|
collectStatistics: false,
|
|
525
552
|
collectCallMetadata: true,
|
|
526
553
|
});
|
|
527
|
-
const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config);
|
|
528
|
-
const processor = new PublicProcessor(
|
|
554
|
+
const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings2);
|
|
555
|
+
const processor = new PublicProcessor(
|
|
556
|
+
globals,
|
|
557
|
+
guardedMerkleTrees,
|
|
558
|
+
contractsDB,
|
|
559
|
+
simulator,
|
|
560
|
+
new TestDateProvider(),
|
|
561
|
+
undefined,
|
|
562
|
+
createLogger('simulator:public-processor', bindings2),
|
|
563
|
+
);
|
|
529
564
|
|
|
530
565
|
// We're simulating a scenario in which private execution immediately enqueues a public call and halts. The private
|
|
531
566
|
// kernel init would in this case inject a nullifier with the transaction request hash as a non-revertible
|
|
@@ -556,7 +591,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
556
591
|
constantData,
|
|
557
592
|
/*gasUsed=*/ new Gas(0, 0),
|
|
558
593
|
/*feePayer=*/ AztecAddress.zero(),
|
|
559
|
-
/*
|
|
594
|
+
/*expirationTimestamp=*/ 0n,
|
|
560
595
|
inputsForPublic,
|
|
561
596
|
undefined,
|
|
562
597
|
);
|
|
@@ -624,36 +659,40 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
624
659
|
return returnValues ?? [];
|
|
625
660
|
}
|
|
626
661
|
|
|
627
|
-
async
|
|
628
|
-
targetContractAddress: AztecAddress,
|
|
629
|
-
functionSelector: FunctionSelector,
|
|
630
|
-
args: Fr[],
|
|
631
|
-
) {
|
|
662
|
+
async txeExecuteUtilityFunction(targetContractAddress: AztecAddress, functionSelector: FunctionSelector, args: Fr[]) {
|
|
632
663
|
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
633
664
|
if (!artifact) {
|
|
634
665
|
throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
|
|
635
666
|
}
|
|
636
667
|
|
|
637
668
|
// Sync notes before executing utility function to discover notes from previous transactions
|
|
638
|
-
await this.
|
|
639
|
-
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
const call = new FunctionCall(
|
|
643
|
-
artifact.name,
|
|
669
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
670
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(
|
|
644
671
|
targetContractAddress,
|
|
645
672
|
functionSelector,
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
673
|
+
async (call, execScopes) => {
|
|
674
|
+
await this.executeUtilityCall(call, execScopes);
|
|
675
|
+
},
|
|
676
|
+
blockHeader,
|
|
677
|
+
this.jobId,
|
|
678
|
+
'ALL_SCOPES',
|
|
651
679
|
);
|
|
652
680
|
|
|
653
|
-
|
|
681
|
+
const call = FunctionCall.from({
|
|
682
|
+
name: artifact.name,
|
|
683
|
+
to: targetContractAddress,
|
|
684
|
+
selector: functionSelector,
|
|
685
|
+
type: FunctionType.UTILITY,
|
|
686
|
+
hideMsgSender: false,
|
|
687
|
+
isStatic: false,
|
|
688
|
+
args,
|
|
689
|
+
returnTypes: [],
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
return this.executeUtilityCall(call, 'ALL_SCOPES');
|
|
654
693
|
}
|
|
655
694
|
|
|
656
|
-
private async executeUtilityCall(call: FunctionCall): Promise<Fr[]> {
|
|
695
|
+
private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes): Promise<Fr[]> {
|
|
657
696
|
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
658
697
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
659
698
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
@@ -666,22 +705,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
666
705
|
|
|
667
706
|
try {
|
|
668
707
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
669
|
-
const oracle = new UtilityExecutionOracle(
|
|
670
|
-
call.to,
|
|
671
|
-
[],
|
|
672
|
-
[],
|
|
708
|
+
const oracle = new UtilityExecutionOracle({
|
|
709
|
+
contractAddress: call.to,
|
|
710
|
+
authWitnesses: [],
|
|
711
|
+
capsules: [],
|
|
673
712
|
anchorBlockHeader,
|
|
674
|
-
this.contractStore,
|
|
675
|
-
this.noteStore,
|
|
676
|
-
this.keyStore,
|
|
677
|
-
this.addressStore,
|
|
678
|
-
this.stateMachine.node,
|
|
679
|
-
this.
|
|
680
|
-
this.
|
|
681
|
-
this.
|
|
682
|
-
this.
|
|
683
|
-
this.
|
|
684
|
-
|
|
713
|
+
contractStore: this.contractStore,
|
|
714
|
+
noteStore: this.noteStore,
|
|
715
|
+
keyStore: this.keyStore,
|
|
716
|
+
addressStore: this.addressStore,
|
|
717
|
+
aztecNode: this.stateMachine.node,
|
|
718
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
719
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
720
|
+
capsuleStore: this.capsuleStore,
|
|
721
|
+
privateEventStore: this.privateEventStore,
|
|
722
|
+
jobId: this.jobId,
|
|
723
|
+
scopes,
|
|
724
|
+
});
|
|
685
725
|
const acirExecutionResult = await new WASMSimulator()
|
|
686
726
|
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
687
727
|
.catch((err: Error) => {
|
|
@@ -697,10 +737,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
697
737
|
);
|
|
698
738
|
});
|
|
699
739
|
|
|
700
|
-
this.logger.verbose(`Utility
|
|
740
|
+
this.logger.verbose(`Utility execution for ${call.to}.${call.selector} completed`);
|
|
701
741
|
return witnessMapToFields(acirExecutionResult.returnWitness);
|
|
702
742
|
} catch (err) {
|
|
703
|
-
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility
|
|
743
|
+
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility execution'));
|
|
704
744
|
}
|
|
705
745
|
}
|
|
706
746
|
|