@aztec/txe 0.69.1 → 0.71.0
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.js +2 -2
- package/dest/node/txe_node.d.ts +10 -5
- package/dest/node/txe_node.d.ts.map +1 -1
- package/dest/node/txe_node.js +43 -19
- package/dest/oracle/txe_oracle.d.ts +21 -13
- package/dest/oracle/txe_oracle.d.ts.map +1 -1
- package/dest/oracle/txe_oracle.js +93 -74
- package/dest/txe_service/txe_service.d.ts +19 -10
- package/dest/txe_service/txe_service.d.ts.map +1 -1
- package/dest/txe_service/txe_service.js +44 -25
- package/dest/util/txe_world_state_db.d.ts +1 -1
- package/dest/util/txe_world_state_db.d.ts.map +1 -1
- package/dest/util/txe_world_state_db.js +2 -2
- package/package.json +14 -14
- package/src/index.ts +1 -1
- package/src/node/txe_node.ts +67 -21
- package/src/oracle/txe_oracle.ts +149 -97
- package/src/txe_service/txe_service.ts +63 -30
- package/src/util/txe_world_state_db.ts +1 -1
package/src/oracle/txe_oracle.ts
CHANGED
|
@@ -58,31 +58,32 @@ import {
|
|
|
58
58
|
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
59
59
|
import { poseidon2Hash } from '@aztec/foundation/crypto';
|
|
60
60
|
import { Fr } from '@aztec/foundation/fields';
|
|
61
|
-
import { type Logger, applyStringFormatting } from '@aztec/foundation/log';
|
|
61
|
+
import { type LogFn, type Logger, applyStringFormatting, createDebugOnlyLogger } from '@aztec/foundation/log';
|
|
62
62
|
import { Timer } from '@aztec/foundation/timer';
|
|
63
63
|
import { type KeyStore } from '@aztec/key-store';
|
|
64
64
|
import { ContractDataOracle, SimulatorOracle, enrichPublicSimulationError } from '@aztec/pxe';
|
|
65
65
|
import {
|
|
66
|
-
ExecutionError,
|
|
67
66
|
ExecutionNoteCache,
|
|
68
67
|
type MessageLoadOracleInputs,
|
|
69
68
|
type NoteData,
|
|
70
69
|
Oracle,
|
|
71
|
-
type PackedValuesCache,
|
|
72
|
-
type PublicTxResult,
|
|
73
|
-
PublicTxSimulator,
|
|
74
70
|
type TypedOracle,
|
|
75
|
-
|
|
76
|
-
createSimulationError,
|
|
71
|
+
WASMSimulator,
|
|
77
72
|
extractCallStack,
|
|
78
73
|
extractPrivateCircuitPublicInputs,
|
|
79
74
|
pickNotes,
|
|
80
|
-
resolveAssertionMessageFromError,
|
|
81
75
|
toACVMWitness,
|
|
82
76
|
witnessMapToFields,
|
|
83
|
-
} from '@aztec/simulator';
|
|
84
|
-
import {
|
|
85
|
-
import {
|
|
77
|
+
} from '@aztec/simulator/client';
|
|
78
|
+
import { createTxForPublicCalls } from '@aztec/simulator/public/fixtures';
|
|
79
|
+
import {
|
|
80
|
+
ExecutionError,
|
|
81
|
+
type HashedValuesCache,
|
|
82
|
+
type PublicTxResult,
|
|
83
|
+
PublicTxSimulator,
|
|
84
|
+
createSimulationError,
|
|
85
|
+
resolveAssertionMessageFromError,
|
|
86
|
+
} from '@aztec/simulator/server';
|
|
86
87
|
import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state';
|
|
87
88
|
|
|
88
89
|
import { TXENode } from '../node/txe_node.js';
|
|
@@ -103,32 +104,48 @@ export class TXE implements TypedOracle {
|
|
|
103
104
|
private contractDataOracle: ContractDataOracle;
|
|
104
105
|
private simulatorOracle: SimulatorOracle;
|
|
105
106
|
|
|
106
|
-
private version: Fr = Fr.ONE;
|
|
107
|
-
private chainId: Fr = Fr.ONE;
|
|
108
|
-
|
|
109
107
|
private uniqueNoteHashesFromPublic: Fr[] = [];
|
|
110
108
|
private siloedNullifiersFromPublic: Fr[] = [];
|
|
111
|
-
private siloedNullifiersFromPrivate: Set<string> = new Set();
|
|
112
109
|
private privateLogs: PrivateLog[] = [];
|
|
113
110
|
private publicLogs: UnencryptedL2Log[] = [];
|
|
114
111
|
|
|
115
112
|
private committedBlocks = new Set<number>();
|
|
116
113
|
|
|
117
|
-
private
|
|
114
|
+
private VERSION = 1;
|
|
115
|
+
private CHAIN_ID = 1;
|
|
116
|
+
|
|
117
|
+
private node: TXENode;
|
|
118
|
+
|
|
119
|
+
private simulationProvider = new WASMSimulator();
|
|
120
|
+
|
|
121
|
+
private noteCache: ExecutionNoteCache;
|
|
122
|
+
|
|
123
|
+
debug: LogFn;
|
|
118
124
|
|
|
119
125
|
constructor(
|
|
120
126
|
private logger: Logger,
|
|
121
127
|
private trees: MerkleTrees,
|
|
122
|
-
private
|
|
123
|
-
private noteCache: ExecutionNoteCache,
|
|
128
|
+
private executionCache: HashedValuesCache,
|
|
124
129
|
private keyStore: KeyStore,
|
|
125
130
|
private txeDatabase: TXEDatabase,
|
|
126
131
|
) {
|
|
132
|
+
this.noteCache = new ExecutionNoteCache(this.getTxRequestHash());
|
|
127
133
|
this.contractDataOracle = new ContractDataOracle(txeDatabase);
|
|
128
134
|
this.contractAddress = AztecAddress.random();
|
|
135
|
+
|
|
136
|
+
this.node = new TXENode(this.blockNumber, this.VERSION, this.CHAIN_ID, this.trees);
|
|
137
|
+
|
|
129
138
|
// Default msg_sender (for entrypoints) is now Fr.max_value rather than 0 addr (see #7190 & #7404)
|
|
130
139
|
this.msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE);
|
|
131
|
-
this.simulatorOracle = new SimulatorOracle(
|
|
140
|
+
this.simulatorOracle = new SimulatorOracle(
|
|
141
|
+
this.contractDataOracle,
|
|
142
|
+
txeDatabase,
|
|
143
|
+
keyStore,
|
|
144
|
+
this.node,
|
|
145
|
+
this.simulationProvider,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
this.debug = createDebugOnlyLogger('aztec:kv-pxe-database');
|
|
132
149
|
}
|
|
133
150
|
|
|
134
151
|
// Utils
|
|
@@ -141,12 +158,12 @@ export class TXE implements TypedOracle {
|
|
|
141
158
|
return db;
|
|
142
159
|
}
|
|
143
160
|
|
|
144
|
-
getChainId() {
|
|
145
|
-
return Promise.resolve(this.
|
|
161
|
+
getChainId(): Promise<Fr> {
|
|
162
|
+
return Promise.resolve(this.node.getChainId().then(id => new Fr(id)));
|
|
146
163
|
}
|
|
147
164
|
|
|
148
|
-
getVersion() {
|
|
149
|
-
return Promise.resolve(this.
|
|
165
|
+
getVersion(): Promise<Fr> {
|
|
166
|
+
return Promise.resolve(this.node.getVersion().then(v => new Fr(v)));
|
|
150
167
|
}
|
|
151
168
|
|
|
152
169
|
getMsgSender() {
|
|
@@ -217,8 +234,8 @@ export class TXE implements TypedOracle {
|
|
|
217
234
|
|
|
218
235
|
const stateReference = await db.getStateReference();
|
|
219
236
|
const inputs = PrivateContextInputs.empty();
|
|
220
|
-
inputs.txContext.chainId = this.
|
|
221
|
-
inputs.txContext.version = this.
|
|
237
|
+
inputs.txContext.chainId = new Fr(await this.node.getChainId());
|
|
238
|
+
inputs.txContext.version = new Fr(await this.node.getVersion());
|
|
222
239
|
inputs.historicalHeader.globalVariables.blockNumber = new Fr(blockNumber);
|
|
223
240
|
inputs.historicalHeader.state = stateReference;
|
|
224
241
|
inputs.historicalHeader.lastArchive.root = Fr.fromBuffer(
|
|
@@ -260,18 +277,14 @@ export class TXE implements TypedOracle {
|
|
|
260
277
|
);
|
|
261
278
|
}
|
|
262
279
|
|
|
263
|
-
async
|
|
280
|
+
async checkNullifiersNotInTree(contractAddress: AztecAddress, nullifiers: Fr[]) {
|
|
264
281
|
const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier));
|
|
265
282
|
const db = await this.trees.getLatest();
|
|
266
283
|
const nullifierIndexesInTree = await db.findLeafIndices(
|
|
267
284
|
MerkleTreeId.NULLIFIER_TREE,
|
|
268
285
|
siloedNullifiers.map(n => n.toBuffer()),
|
|
269
286
|
);
|
|
270
|
-
|
|
271
|
-
const notInCache = siloedNullifiers.every(n => !this.siloedNullifiersFromPrivate.has(n.toString()));
|
|
272
|
-
if (notInTree && notInCache) {
|
|
273
|
-
siloedNullifiers.forEach(n => this.siloedNullifiersFromPrivate.add(n.toString()));
|
|
274
|
-
} else {
|
|
287
|
+
if (nullifierIndexesInTree.some(index => index !== undefined)) {
|
|
275
288
|
throw new Error(`Rejecting tx for emitting duplicate nullifiers`);
|
|
276
289
|
}
|
|
277
290
|
}
|
|
@@ -360,16 +373,16 @@ export class TXE implements TypedOracle {
|
|
|
360
373
|
return Fr.random();
|
|
361
374
|
}
|
|
362
375
|
|
|
363
|
-
|
|
364
|
-
return Promise.resolve(this.
|
|
376
|
+
storeArrayInExecutionCache(values: Fr[]) {
|
|
377
|
+
return Promise.resolve(this.executionCache.store(values));
|
|
365
378
|
}
|
|
366
379
|
|
|
367
|
-
|
|
368
|
-
return Promise.resolve(this.
|
|
380
|
+
storeInExecutionCache(values: Fr[]) {
|
|
381
|
+
return Promise.resolve(this.executionCache.store(values));
|
|
369
382
|
}
|
|
370
383
|
|
|
371
|
-
|
|
372
|
-
return Promise.resolve(this.
|
|
384
|
+
loadFromExecutionCache(returnsHash: Fr) {
|
|
385
|
+
return Promise.resolve(this.executionCache.getPreimage(returnsHash));
|
|
373
386
|
}
|
|
374
387
|
|
|
375
388
|
getKeyValidationRequest(pkMHash: Fr): Promise<KeyValidationRequest> {
|
|
@@ -395,11 +408,11 @@ export class TXE implements TypedOracle {
|
|
|
395
408
|
return [new Fr(index), ...siblingPath.toFields()];
|
|
396
409
|
}
|
|
397
410
|
|
|
398
|
-
async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
411
|
+
// async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) {
|
|
412
|
+
// const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber);
|
|
413
|
+
// const result = await committedDb.getSiblingPath(treeId, leafIndex.toBigInt());
|
|
414
|
+
// return result.toFields();
|
|
415
|
+
// }
|
|
403
416
|
|
|
404
417
|
async getNullifierMembershipWitness(
|
|
405
418
|
blockNumber: number,
|
|
@@ -550,12 +563,18 @@ export class TXE implements TypedOracle {
|
|
|
550
563
|
}
|
|
551
564
|
|
|
552
565
|
async notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) {
|
|
553
|
-
await this.
|
|
566
|
+
await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]);
|
|
554
567
|
this.noteCache.nullifyNote(this.contractAddress, innerNullifier, noteHash);
|
|
555
568
|
this.sideEffectCounter = counter + 1;
|
|
556
569
|
return Promise.resolve();
|
|
557
570
|
}
|
|
558
571
|
|
|
572
|
+
async notifyCreatedNullifier(innerNullifier: Fr): Promise<void> {
|
|
573
|
+
await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]);
|
|
574
|
+
this.noteCache.nullifierCreated(this.contractAddress, innerNullifier);
|
|
575
|
+
return Promise.resolve();
|
|
576
|
+
}
|
|
577
|
+
|
|
559
578
|
async checkNullifierExists(innerNullifier: Fr): Promise<boolean> {
|
|
560
579
|
const nullifier = siloNullifier(this.contractAddress, innerNullifier!);
|
|
561
580
|
const db = await this.trees.getLatest();
|
|
@@ -617,6 +636,7 @@ export class TXE implements TypedOracle {
|
|
|
617
636
|
|
|
618
637
|
async commitState() {
|
|
619
638
|
const blockNumber = await this.getBlockNumber();
|
|
639
|
+
const { usedTxRequestHashForNonces } = this.noteCache.finish();
|
|
620
640
|
if (this.committedBlocks.has(blockNumber)) {
|
|
621
641
|
throw new Error('Already committed state');
|
|
622
642
|
} else {
|
|
@@ -625,30 +645,25 @@ export class TXE implements TypedOracle {
|
|
|
625
645
|
|
|
626
646
|
const txEffect = TxEffect.empty();
|
|
627
647
|
|
|
648
|
+
const nonceGenerator = usedTxRequestHashForNonces ? this.getTxRequestHash() : this.noteCache.getAllNullifiers()[0];
|
|
649
|
+
|
|
628
650
|
let i = 0;
|
|
629
651
|
txEffect.noteHashes = [
|
|
630
652
|
...this.noteCache
|
|
631
653
|
.getAllNotes()
|
|
632
654
|
.map(pendingNote =>
|
|
633
655
|
computeUniqueNoteHash(
|
|
634
|
-
computeNoteHashNonce(
|
|
656
|
+
computeNoteHashNonce(nonceGenerator, i++),
|
|
635
657
|
siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption),
|
|
636
658
|
),
|
|
637
659
|
),
|
|
638
660
|
...this.uniqueNoteHashesFromPublic,
|
|
639
661
|
];
|
|
640
|
-
txEffect.nullifiers = [
|
|
641
|
-
new Fr(blockNumber + 6969),
|
|
642
|
-
...Array.from(this.siloedNullifiersFromPrivate).map(n => Fr.fromString(n)),
|
|
643
|
-
];
|
|
644
|
-
|
|
645
|
-
// Using block number itself, (without adding 6969) gets killed at 1 as it says the slot is already used,
|
|
646
|
-
// it seems like we commit a 1 there to the trees before ? To see what I mean, uncomment these lines below
|
|
647
|
-
// let index = await (await this.trees.getLatest()).findLeafIndex(MerkleTreeId.NULLIFIER_TREE, Fr.ONE.toBuffer());
|
|
648
|
-
// console.log('INDEX OF ONE', index);
|
|
649
|
-
// index = await (await this.trees.getLatest()).findLeafIndex(MerkleTreeId.NULLIFIER_TREE, Fr.random().toBuffer());
|
|
650
|
-
// console.log('INDEX OF RANDOM', index);
|
|
651
662
|
|
|
663
|
+
txEffect.nullifiers = this.noteCache.getAllNullifiers();
|
|
664
|
+
if (usedTxRequestHashForNonces) {
|
|
665
|
+
txEffect.nullifiers.unshift(this.getTxRequestHash());
|
|
666
|
+
}
|
|
652
667
|
this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect);
|
|
653
668
|
this.node.setNullifiersIndexesWithBlock(blockNumber, txEffect.nullifiers);
|
|
654
669
|
this.node.addNoteLogsByTags(this.blockNumber, this.privateLogs);
|
|
@@ -659,10 +674,14 @@ export class TXE implements TypedOracle {
|
|
|
659
674
|
|
|
660
675
|
this.privateLogs = [];
|
|
661
676
|
this.publicLogs = [];
|
|
662
|
-
this.siloedNullifiersFromPrivate = new Set();
|
|
663
677
|
this.uniqueNoteHashesFromPublic = [];
|
|
664
678
|
this.siloedNullifiersFromPublic = [];
|
|
665
|
-
this.noteCache = new ExecutionNoteCache(
|
|
679
|
+
this.noteCache = new ExecutionNoteCache(this.getTxRequestHash());
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
getTxRequestHash() {
|
|
683
|
+
// Using block number itself is invalid since indexed trees come prefilled with the first slots.
|
|
684
|
+
return new Fr(this.blockNumber + 6969);
|
|
666
685
|
}
|
|
667
686
|
|
|
668
687
|
emitContractClassLog(_log: UnencryptedL2Log, _counter: number): Fr {
|
|
@@ -697,21 +716,23 @@ export class TXE implements TypedOracle {
|
|
|
697
716
|
const initialWitness = await this.getInitialWitness(artifact, argsHash, sideEffectCounter, isStaticCall);
|
|
698
717
|
const acvmCallback = new Oracle(this);
|
|
699
718
|
const timer = new Timer();
|
|
700
|
-
const acirExecutionResult = await
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
719
|
+
const acirExecutionResult = await this.simulationProvider
|
|
720
|
+
.executeUserCircuit(acir, initialWitness, acvmCallback)
|
|
721
|
+
.catch((err: Error) => {
|
|
722
|
+
err.message = resolveAssertionMessageFromError(err, artifact);
|
|
723
|
+
|
|
724
|
+
const execError = new ExecutionError(
|
|
725
|
+
err.message,
|
|
726
|
+
{
|
|
727
|
+
contractAddress: targetContractAddress,
|
|
728
|
+
functionSelector,
|
|
729
|
+
},
|
|
730
|
+
extractCallStack(err, artifact.debug),
|
|
731
|
+
{ cause: err },
|
|
732
|
+
);
|
|
733
|
+
this.logger.debug(`Error executing private function ${targetContractAddress}:${functionSelector}`);
|
|
734
|
+
throw createSimulationError(execError);
|
|
735
|
+
});
|
|
715
736
|
const duration = timer.ms();
|
|
716
737
|
const publicInputs = extractPrivateCircuitPublicInputs(artifact, acirExecutionResult.partialWitness);
|
|
717
738
|
|
|
@@ -734,24 +755,6 @@ export class TXE implements TypedOracle {
|
|
|
734
755
|
publicInputs.privateLogs.filter(privateLog => !privateLog.isEmpty()).map(privateLog => privateLog.log),
|
|
735
756
|
);
|
|
736
757
|
|
|
737
|
-
const executionNullifiers = publicInputs.nullifiers
|
|
738
|
-
.filter(nullifier => !nullifier.isEmpty())
|
|
739
|
-
.map(nullifier => nullifier.value);
|
|
740
|
-
// We inject nullifiers into siloedNullifiersFromPrivate from notifyNullifiedNote,
|
|
741
|
-
// so top level calls to destroyNote work as expected. As such, we are certain
|
|
742
|
-
// that we would insert duplicates if we just took the nullifiers from the public inputs and
|
|
743
|
-
// blindly inserted them into siloedNullifiersFromPrivate. To avoid this, we extract the first
|
|
744
|
-
// (and only the first!) duplicated nullifier from the public inputs, so we can just push
|
|
745
|
-
// the ones that were not created by deleting a note
|
|
746
|
-
const firstDuplicateIndexes = executionNullifiers
|
|
747
|
-
.map((nullifier, index) => {
|
|
748
|
-
const siloedNullifier = siloNullifier(targetContractAddress, nullifier);
|
|
749
|
-
return this.siloedNullifiersFromPrivate.has(siloedNullifier.toString()) ? index : -1;
|
|
750
|
-
})
|
|
751
|
-
.filter(index => index !== -1);
|
|
752
|
-
const nonNoteNullifiers = executionNullifiers.filter((_, index) => !firstDuplicateIndexes.includes(index));
|
|
753
|
-
await this.addNullifiersFromPrivate(targetContractAddress, nonNoteNullifiers);
|
|
754
|
-
|
|
755
758
|
this.setContractAddress(currentContractAddress);
|
|
756
759
|
this.setMsgSender(currentMessageSender);
|
|
757
760
|
this.setFunctionSelector(currentFunctionSelector);
|
|
@@ -762,7 +765,7 @@ export class TXE implements TypedOracle {
|
|
|
762
765
|
async getInitialWitness(abi: FunctionAbi, argsHash: Fr, sideEffectCounter: number, isStaticCall: boolean) {
|
|
763
766
|
const argumentsSize = countArgumentsSize(abi);
|
|
764
767
|
|
|
765
|
-
const args = this.
|
|
768
|
+
const args = this.executionCache.getPreimage(argsHash);
|
|
766
769
|
|
|
767
770
|
if (args.length !== argumentsSize) {
|
|
768
771
|
throw new Error('Invalid arguments size');
|
|
@@ -809,8 +812,8 @@ export class TXE implements TypedOracle {
|
|
|
809
812
|
const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this));
|
|
810
813
|
|
|
811
814
|
const globalVariables = GlobalVariables.empty();
|
|
812
|
-
globalVariables.chainId = this.
|
|
813
|
-
globalVariables.version = this.
|
|
815
|
+
globalVariables.chainId = new Fr(await this.node.getChainId());
|
|
816
|
+
globalVariables.version = new Fr(await this.node.getVersion());
|
|
814
817
|
globalVariables.blockNumber = new Fr(this.blockNumber);
|
|
815
818
|
globalVariables.gasFees = new GasFees(1, 1);
|
|
816
819
|
|
|
@@ -829,14 +832,18 @@ export class TXE implements TypedOracle {
|
|
|
829
832
|
const simulator = new PublicTxSimulator(
|
|
830
833
|
db,
|
|
831
834
|
new TXEWorldStateDB(db, new TXEPublicContractDataSource(this)),
|
|
832
|
-
new NoopTelemetryClient(),
|
|
833
835
|
globalVariables,
|
|
834
836
|
);
|
|
835
837
|
|
|
836
838
|
// When setting up a teardown call, we tell it that
|
|
837
839
|
// private execution used Gas(1, 1) so it can compute a tx fee.
|
|
838
840
|
const gasUsedByPrivate = isTeardown ? new Gas(1, 1) : Gas.empty();
|
|
839
|
-
const tx =
|
|
841
|
+
const tx = createTxForPublicCalls(
|
|
842
|
+
/*setupExecutionRequests=*/ [],
|
|
843
|
+
/*appExecutionRequests=*/ isTeardown ? [] : [executionRequest],
|
|
844
|
+
/*teardownExecutionRequests=*/ isTeardown ? executionRequest : undefined,
|
|
845
|
+
gasUsedByPrivate,
|
|
846
|
+
);
|
|
840
847
|
|
|
841
848
|
const result = await simulator.simulate(tx);
|
|
842
849
|
|
|
@@ -868,8 +875,8 @@ export class TXE implements TypedOracle {
|
|
|
868
875
|
isStaticCall,
|
|
869
876
|
);
|
|
870
877
|
|
|
871
|
-
const args = [this.functionSelector.toField(), ...this.
|
|
872
|
-
const newArgsHash = this.
|
|
878
|
+
const args = [this.functionSelector.toField(), ...this.executionCache.getPreimage(argsHash)];
|
|
879
|
+
const newArgsHash = this.executionCache.store(args);
|
|
873
880
|
|
|
874
881
|
const executionResult = await this.executePublicFunction(args, callContext, isTeardown);
|
|
875
882
|
|
|
@@ -968,6 +975,19 @@ export class TXE implements TypedOracle {
|
|
|
968
975
|
return Promise.resolve();
|
|
969
976
|
}
|
|
970
977
|
|
|
978
|
+
deliverNote(
|
|
979
|
+
_contractAddress: AztecAddress,
|
|
980
|
+
_storageSlot: Fr,
|
|
981
|
+
_nonce: Fr,
|
|
982
|
+
_content: Fr[],
|
|
983
|
+
_noteHash: Fr,
|
|
984
|
+
_nullifier: Fr,
|
|
985
|
+
_txHash: Fr,
|
|
986
|
+
_recipient: AztecAddress,
|
|
987
|
+
): Promise<void> {
|
|
988
|
+
throw new Error('deliverNote');
|
|
989
|
+
}
|
|
990
|
+
|
|
971
991
|
// AVM oracles
|
|
972
992
|
|
|
973
993
|
async avmOpcodeCall(targetContractAddress: AztecAddress, args: Fr[], isStaticCall: boolean): Promise<PublicTxResult> {
|
|
@@ -1051,4 +1071,36 @@ export class TXE implements TypedOracle {
|
|
|
1051
1071
|
|
|
1052
1072
|
return preimage.value;
|
|
1053
1073
|
}
|
|
1074
|
+
|
|
1075
|
+
dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void> {
|
|
1076
|
+
if (!contractAddress.equals(this.contractAddress)) {
|
|
1077
|
+
// TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
|
|
1078
|
+
throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
|
|
1079
|
+
}
|
|
1080
|
+
return this.txeDatabase.dbStore(this.contractAddress, slot, values);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
|
|
1084
|
+
if (!contractAddress.equals(this.contractAddress)) {
|
|
1085
|
+
// TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
|
|
1086
|
+
throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
|
|
1087
|
+
}
|
|
1088
|
+
return this.txeDatabase.dbLoad(this.contractAddress, slot);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void> {
|
|
1092
|
+
if (!contractAddress.equals(this.contractAddress)) {
|
|
1093
|
+
// TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
|
|
1094
|
+
throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
|
|
1095
|
+
}
|
|
1096
|
+
return this.txeDatabase.dbDelete(this.contractAddress, slot);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
|
|
1100
|
+
if (!contractAddress.equals(this.contractAddress)) {
|
|
1101
|
+
// TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
|
|
1102
|
+
throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
|
|
1103
|
+
}
|
|
1104
|
+
return this.txeDatabase.dbCopy(this.contractAddress, srcSlot, dstSlot, numEntries);
|
|
1105
|
+
}
|
|
1054
1106
|
}
|
|
@@ -18,8 +18,9 @@ import { openTmpStore } from '@aztec/kv-store/lmdb';
|
|
|
18
18
|
import { protocolContractNames } from '@aztec/protocol-contracts';
|
|
19
19
|
import { getCanonicalProtocolContract } from '@aztec/protocol-contracts/bundle';
|
|
20
20
|
import { enrichPublicSimulationError } from '@aztec/pxe';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
21
|
+
import { type TypedOracle } from '@aztec/simulator/client';
|
|
22
|
+
import { HashedValuesCache } from '@aztec/simulator/server';
|
|
23
|
+
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
23
24
|
import { MerkleTrees } from '@aztec/world-state';
|
|
24
25
|
|
|
25
26
|
import { TXE } from '../oracle/txe_oracle.js';
|
|
@@ -41,10 +42,8 @@ export class TXEService {
|
|
|
41
42
|
|
|
42
43
|
static async init(logger: Logger) {
|
|
43
44
|
const store = openTmpStore(true);
|
|
44
|
-
const trees = await MerkleTrees.new(store,
|
|
45
|
-
const
|
|
46
|
-
const txHash = new Fr(1); // The txHash is used for computing the revertible nullifiers for non-revertible note hashes. It can be any value for testing.
|
|
47
|
-
const noteCache = new ExecutionNoteCache(txHash);
|
|
45
|
+
const trees = await MerkleTrees.new(store, getTelemetryClient(), logger);
|
|
46
|
+
const executionCache = new HashedValuesCache();
|
|
48
47
|
const keyStore = new KeyStore(store);
|
|
49
48
|
const txeDatabase = new TXEDatabase(store);
|
|
50
49
|
// Register protocol contracts.
|
|
@@ -54,7 +53,7 @@ export class TXEService {
|
|
|
54
53
|
await txeDatabase.addContractInstance(instance);
|
|
55
54
|
}
|
|
56
55
|
logger.debug(`TXE service initialized`);
|
|
57
|
-
const txe = new TXE(logger, trees,
|
|
56
|
+
const txe = new TXE(logger, trees, executionCache, keyStore, txeDatabase);
|
|
58
57
|
const service = new TXEService(logger, txe);
|
|
59
58
|
await service.advanceBlocksBy(toSingle(new Fr(1n)));
|
|
60
59
|
return service;
|
|
@@ -272,25 +271,20 @@ export class TXEService {
|
|
|
272
271
|
return toForeignCallResult([toSingle(new Fr(blockNumber))]);
|
|
273
272
|
}
|
|
274
273
|
|
|
275
|
-
async
|
|
276
|
-
const
|
|
277
|
-
return toForeignCallResult([toSingle(
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
async packArguments(_length: ForeignCallSingle, values: ForeignCallArray) {
|
|
281
|
-
const packed = await this.typedOracle.packArgumentsArray(fromArray(values));
|
|
282
|
-
return toForeignCallResult([toSingle(packed)]);
|
|
274
|
+
async storeArrayInExecutionCache(args: ForeignCallArray) {
|
|
275
|
+
const hash = await this.typedOracle.storeArrayInExecutionCache(fromArray(args));
|
|
276
|
+
return toForeignCallResult([toSingle(hash)]);
|
|
283
277
|
}
|
|
284
278
|
|
|
285
279
|
// Since the argument is a slice, noir automatically adds a length field to oracle call.
|
|
286
|
-
async
|
|
287
|
-
const
|
|
288
|
-
return toForeignCallResult([toSingle(
|
|
280
|
+
async storeInExecutionCache(_length: ForeignCallSingle, values: ForeignCallArray) {
|
|
281
|
+
const returnsHash = await this.typedOracle.storeInExecutionCache(fromArray(values));
|
|
282
|
+
return toForeignCallResult([toSingle(returnsHash)]);
|
|
289
283
|
}
|
|
290
284
|
|
|
291
|
-
async
|
|
292
|
-
const
|
|
293
|
-
return toForeignCallResult([toArray(
|
|
285
|
+
async loadFromExecutionCache(hash: ForeignCallSingle) {
|
|
286
|
+
const returns = await this.typedOracle.loadFromExecutionCache(fromSingle(hash));
|
|
287
|
+
return toForeignCallResult([toArray(returns)]);
|
|
294
288
|
}
|
|
295
289
|
|
|
296
290
|
// Since the argument is a slice, noir automatically adds a length field to oracle call.
|
|
@@ -334,15 +328,6 @@ export class TXEService {
|
|
|
334
328
|
return toForeignCallResult([toArray(witness.toFields())]);
|
|
335
329
|
}
|
|
336
330
|
|
|
337
|
-
async getSiblingPath(blockNumber: ForeignCallSingle, treeId: ForeignCallSingle, leafIndex: ForeignCallSingle) {
|
|
338
|
-
const result = await this.typedOracle.getSiblingPath(
|
|
339
|
-
fromSingle(blockNumber).toNumber(),
|
|
340
|
-
fromSingle(treeId).toNumber(),
|
|
341
|
-
fromSingle(leafIndex),
|
|
342
|
-
);
|
|
343
|
-
return toForeignCallResult([toArray(result)]);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
331
|
async getNotes(
|
|
347
332
|
storageSlot: ForeignCallSingle,
|
|
348
333
|
numSelects: ForeignCallSingle,
|
|
@@ -434,6 +419,11 @@ export class TXEService {
|
|
|
434
419
|
return toForeignCallResult([toSingle(new Fr(0))]);
|
|
435
420
|
}
|
|
436
421
|
|
|
422
|
+
async notifyCreatedNullifier(innerNullifier: ForeignCallSingle) {
|
|
423
|
+
await this.typedOracle.notifyCreatedNullifier(fromSingle(innerNullifier));
|
|
424
|
+
return toForeignCallResult([toSingle(new Fr(0))]);
|
|
425
|
+
}
|
|
426
|
+
|
|
437
427
|
async checkNullifierExists(innerNullifier: ForeignCallSingle) {
|
|
438
428
|
const exists = await this.typedOracle.checkNullifierExists(fromSingle(innerNullifier));
|
|
439
429
|
return toForeignCallResult([toSingle(new Fr(exists))]);
|
|
@@ -598,6 +588,49 @@ export class TXEService {
|
|
|
598
588
|
return toForeignCallResult([]);
|
|
599
589
|
}
|
|
600
590
|
|
|
591
|
+
async dbStore(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, values: ForeignCallArray) {
|
|
592
|
+
await this.typedOracle.dbStore(
|
|
593
|
+
AztecAddress.fromField(fromSingle(contractAddress)),
|
|
594
|
+
fromSingle(slot),
|
|
595
|
+
fromArray(values),
|
|
596
|
+
);
|
|
597
|
+
return toForeignCallResult([]);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
async dbLoad(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, tSize: ForeignCallSingle) {
|
|
601
|
+
const values = await this.typedOracle.dbLoad(AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(slot));
|
|
602
|
+
// We are going to return a Noir Option struct to represent the possibility of null values. Options are a struct
|
|
603
|
+
// with two fields: `some` (a boolean) and `value` (a field array in this case).
|
|
604
|
+
if (values === null) {
|
|
605
|
+
// No data was found so we set `some` to 0 and pad `value` with zeros get the correct return size.
|
|
606
|
+
return toForeignCallResult([toSingle(new Fr(0)), toArray(Array(fromSingle(tSize).toNumber()).fill(new Fr(0)))]);
|
|
607
|
+
} else {
|
|
608
|
+
// Data was found so we set `some` to 1 and return it along with `value`.
|
|
609
|
+
return toForeignCallResult([toSingle(new Fr(1)), toArray(values)]);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
async dbDelete(contractAddress: ForeignCallSingle, slot: ForeignCallSingle) {
|
|
614
|
+
await this.typedOracle.dbDelete(AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(slot));
|
|
615
|
+
return toForeignCallResult([]);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
async dbCopy(
|
|
619
|
+
contractAddress: ForeignCallSingle,
|
|
620
|
+
srcSlot: ForeignCallSingle,
|
|
621
|
+
dstSlot: ForeignCallSingle,
|
|
622
|
+
numEntries: ForeignCallSingle,
|
|
623
|
+
) {
|
|
624
|
+
await this.typedOracle.dbCopy(
|
|
625
|
+
AztecAddress.fromField(fromSingle(contractAddress)),
|
|
626
|
+
fromSingle(srcSlot),
|
|
627
|
+
fromSingle(dstSlot),
|
|
628
|
+
fromSingle(numEntries).toNumber(),
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
return toForeignCallResult([]);
|
|
632
|
+
}
|
|
633
|
+
|
|
601
634
|
// AVM opcodes
|
|
602
635
|
|
|
603
636
|
avmOpcodeEmitUnencryptedLog(_message: ForeignCallArray) {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type PublicDataTreeLeafPreimage,
|
|
8
8
|
} from '@aztec/circuits.js';
|
|
9
9
|
import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash';
|
|
10
|
-
import { WorldStateDB } from '@aztec/simulator';
|
|
10
|
+
import { WorldStateDB } from '@aztec/simulator/server';
|
|
11
11
|
|
|
12
12
|
export class TXEWorldStateDB extends WorldStateDB {
|
|
13
13
|
constructor(private merkleDb: MerkleTreeWriteOperations, dataSource: ContractDataSource) {
|