@aztec/txe 0.59.0 → 0.61.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.
@@ -7,6 +7,7 @@ import {
7
7
  PublicDataWitness,
8
8
  PublicDataWrite,
9
9
  PublicExecutionRequest,
10
+ SimulationError,
10
11
  type UnencryptedL2Log,
11
12
  } from '@aztec/circuit-types';
12
13
  import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats';
@@ -17,6 +18,7 @@ import {
17
18
  type ContractInstanceWithAddress,
18
19
  Gas,
19
20
  Header,
21
+ IndexedTaggingSecret,
20
22
  type KeyValidationRequest,
21
23
  NULLIFIER_SUBTREE_HEIGHT,
22
24
  type NULLIFIER_TREE_HEIGHT,
@@ -29,6 +31,7 @@ import {
29
31
  type PublicDataTreeLeafPreimage,
30
32
  TxContext,
31
33
  computeContractClassId,
34
+ computeTaggingSecret,
32
35
  deriveKeys,
33
36
  getContractClassFromArtifact,
34
37
  } from '@aztec/circuits.js';
@@ -42,11 +45,12 @@ import {
42
45
  countArgumentsSize,
43
46
  } from '@aztec/foundation/abi';
44
47
  import { AztecAddress } from '@aztec/foundation/aztec-address';
48
+ import { poseidon2Hash } from '@aztec/foundation/crypto';
45
49
  import { Fr } from '@aztec/foundation/fields';
46
50
  import { type Logger, applyStringFormatting } from '@aztec/foundation/log';
47
51
  import { Timer } from '@aztec/foundation/timer';
48
52
  import { type KeyStore } from '@aztec/key-store';
49
- import { ContractDataOracle } from '@aztec/pxe';
53
+ import { ContractDataOracle, enrichPublicSimulationError } from '@aztec/pxe';
50
54
  import {
51
55
  ExecutionError,
52
56
  type ExecutionNoteCache,
@@ -78,9 +82,8 @@ export class TXE implements TypedOracle {
78
82
  private msgSender: AztecAddress;
79
83
  private functionSelector = FunctionSelector.fromField(new Fr(0));
80
84
  private isStaticCall = false;
81
- // This will hold the _real_ calldata. That is, the one without the PublicContextInputs.
82
- // TODO: Remove this comment once PublicContextInputs are removed.
83
- private calldata: Fr[] = [];
85
+ // Return/revert data of the latest nested call.
86
+ private nestedCallReturndata: Fr[] = [];
84
87
 
85
88
  private contractDataOracle: ContractDataOracle;
86
89
 
@@ -127,18 +130,10 @@ export class TXE implements TypedOracle {
127
130
  return this.functionSelector;
128
131
  }
129
132
 
130
- getCalldata() {
131
- return this.calldata;
132
- }
133
-
134
133
  setMsgSender(msgSender: Fr) {
135
134
  this.msgSender = msgSender;
136
135
  }
137
136
 
138
- setCalldata(calldata: Fr[]) {
139
- this.calldata = calldata;
140
- }
141
-
142
137
  setFunctionSelector(functionSelector: FunctionSelector) {
143
138
  this.functionSelector = functionSelector;
144
139
  }
@@ -188,48 +183,24 @@ export class TXE implements TypedOracle {
188
183
  blockNumber: number,
189
184
  sideEffectsCounter = this.sideEffectsCounter,
190
185
  isStaticCall = false,
191
- isDelegateCall = false,
192
186
  ) {
193
187
  const db = await this.#getTreesAt(blockNumber);
194
188
  const previousBlockState = await this.#getTreesAt(blockNumber - 1);
195
189
 
196
190
  const stateReference = await db.getStateReference();
197
191
  const inputs = PrivateContextInputs.empty();
192
+ inputs.txContext.chainId = this.chainId;
193
+ inputs.txContext.version = this.version;
198
194
  inputs.historicalHeader.globalVariables.blockNumber = new Fr(blockNumber);
199
195
  inputs.historicalHeader.state = stateReference;
200
196
  inputs.historicalHeader.lastArchive.root = Fr.fromBuffer(
201
197
  (await previousBlockState.getTreeInfo(MerkleTreeId.ARCHIVE)).root,
202
198
  );
203
- inputs.callContext.msgSender = this.msgSender;
204
- inputs.callContext.storageContractAddress = this.contractAddress;
205
- inputs.callContext.isStaticCall = isStaticCall;
206
- inputs.callContext.isDelegateCall = isDelegateCall;
199
+ inputs.callContext = new CallContext(this.msgSender, this.contractAddress, this.functionSelector, isStaticCall);
207
200
  inputs.startSideEffectCounter = sideEffectsCounter;
208
- inputs.callContext.functionSelector = this.functionSelector;
209
201
  return inputs;
210
202
  }
211
203
 
212
- async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean> {
213
- const nullifier = siloNullifier(targetAddress, innerNullifier!);
214
- const db = await this.trees.getLatest();
215
- const index = await db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer());
216
- return index !== undefined;
217
- }
218
-
219
- async avmOpcodeEmitNullifier(nullifier: Fr) {
220
- const db = await this.trees.getLatest();
221
- const siloedNullifier = siloNullifier(this.contractAddress, nullifier);
222
- await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT);
223
- return Promise.resolve();
224
- }
225
-
226
- async avmOpcodeEmitNoteHash(noteHash: Fr) {
227
- const db = await this.trees.getLatest();
228
- const siloedNoteHash = siloNoteHash(this.contractAddress, noteHash);
229
- await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [siloedNoteHash]);
230
- return Promise.resolve();
231
- }
232
-
233
204
  deriveKeys(secret: Fr) {
234
205
  return deriveKeys(secret);
235
206
  }
@@ -468,24 +439,6 @@ export class TXE implements TypedOracle {
468
439
  throw new Error('Method not implemented.');
469
440
  }
470
441
 
471
- async avmOpcodeStorageRead(slot: Fr) {
472
- const db = await this.trees.getLatest();
473
-
474
- const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, slot);
475
-
476
- const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
477
- if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
478
- return Fr.ZERO;
479
- }
480
-
481
- const preimage = (await db.getLeafPreimage(
482
- MerkleTreeId.PUBLIC_DATA_TREE,
483
- lowLeafResult.index,
484
- )) as PublicDataTreeLeafPreimage;
485
-
486
- return preimage.value;
487
- }
488
-
489
442
  async storageRead(
490
443
  contractAddress: Fr,
491
444
  startStorageSlot: Fr,
@@ -555,13 +508,12 @@ export class TXE implements TypedOracle {
555
508
  argsHash: Fr,
556
509
  sideEffectCounter: number,
557
510
  isStaticCall: boolean,
558
- isDelegateCall: boolean,
559
511
  ) {
560
512
  this.logger.verbose(
561
513
  `Executing external function ${await this.getDebugFunctionName(
562
514
  targetContractAddress,
563
515
  functionSelector,
564
- )}@${targetContractAddress} isStaticCall=${isStaticCall} isDelegateCall=${isDelegateCall}`,
516
+ )}@${targetContractAddress} isStaticCall=${isStaticCall}`,
565
517
  );
566
518
 
567
519
  // Store and modify env
@@ -575,13 +527,7 @@ export class TXE implements TypedOracle {
575
527
  const artifact = await this.contractDataOracle.getFunctionArtifact(targetContractAddress, functionSelector);
576
528
 
577
529
  const acir = artifact.bytecode;
578
- const initialWitness = await this.getInitialWitness(
579
- artifact,
580
- argsHash,
581
- sideEffectCounter,
582
- isStaticCall,
583
- isDelegateCall,
584
- );
530
+ const initialWitness = await this.getInitialWitness(artifact, argsHash, sideEffectCounter, isStaticCall);
585
531
  const acvmCallback = new Oracle(this);
586
532
  const timer = new Timer();
587
533
  try {
@@ -633,13 +579,7 @@ export class TXE implements TypedOracle {
633
579
  }
634
580
  }
635
581
 
636
- async getInitialWitness(
637
- abi: FunctionAbi,
638
- argsHash: Fr,
639
- sideEffectCounter: number,
640
- isStaticCall: boolean,
641
- isDelegateCall: boolean,
642
- ) {
582
+ async getInitialWitness(abi: FunctionAbi, argsHash: Fr, sideEffectCounter: number, isStaticCall: boolean) {
643
583
  const argumentsSize = countArgumentsSize(abi);
644
584
 
645
585
  const args = this.packedValuesCache.unpack(argsHash);
@@ -652,7 +592,6 @@ export class TXE implements TypedOracle {
652
592
  this.blockNumber - 1,
653
593
  sideEffectCounter,
654
594
  isStaticCall,
655
- isDelegateCall,
656
595
  );
657
596
 
658
597
  const fields = [...privateContextInputs.toFields(), ...args];
@@ -680,21 +619,34 @@ export class TXE implements TypedOracle {
680
619
  return `${artifact.name}:${f.name}`;
681
620
  }
682
621
 
683
- async executePublicFunction(
684
- targetContractAddress: AztecAddress,
685
- args: Fr[],
686
- callContext: CallContext,
687
- counter: number,
688
- ) {
622
+ private async executePublicFunction(args: Fr[], callContext: CallContext, counter: number) {
689
623
  const executor = new PublicExecutor(
690
624
  new TXEWorldStateDB(await this.trees.getLatest(), new TXEPublicContractDataSource(this)),
691
625
  new NoopTelemetryClient(),
692
626
  );
693
- const execution = new PublicExecutionRequest(targetContractAddress, callContext, args);
627
+ const execution = new PublicExecutionRequest(callContext, args);
628
+
629
+ const db = await this.trees.getLatest();
630
+ const previousBlockState = await this.#getTreesAt(this.blockNumber - 1);
631
+
632
+ const combinedConstantData = CombinedConstantData.empty();
633
+ combinedConstantData.globalVariables.chainId = this.chainId;
634
+ combinedConstantData.globalVariables.version = this.version;
635
+ combinedConstantData.globalVariables.blockNumber = new Fr(this.blockNumber);
636
+ combinedConstantData.historicalHeader.globalVariables.chainId = this.chainId;
637
+ combinedConstantData.historicalHeader.globalVariables.version = this.version;
638
+ combinedConstantData.historicalHeader.globalVariables.blockNumber = new Fr(this.blockNumber - 1);
639
+ combinedConstantData.historicalHeader.state = await db.getStateReference();
640
+ combinedConstantData.historicalHeader.lastArchive.root = Fr.fromBuffer(
641
+ (await previousBlockState.getTreeInfo(MerkleTreeId.ARCHIVE)).root,
642
+ );
643
+
644
+ combinedConstantData.txContext.chainId = this.chainId;
645
+ combinedConstantData.txContext.version = this.version;
694
646
 
695
647
  const executionResult = executor.simulate(
696
648
  execution,
697
- CombinedConstantData.empty(),
649
+ combinedConstantData,
698
650
  Gas.test(),
699
651
  TxContext.empty(),
700
652
  /* pendingNullifiers */ [],
@@ -704,54 +656,12 @@ export class TXE implements TypedOracle {
704
656
  return Promise.resolve(executionResult);
705
657
  }
706
658
 
707
- async avmOpcodeCall(
708
- targetContractAddress: AztecAddress,
709
- functionSelector: FunctionSelector,
710
- args: Fr[],
711
- isStaticCall: boolean,
712
- isDelegateCall: boolean,
713
- ) {
714
- // Store and modify env
715
- const currentContractAddress = AztecAddress.fromField(this.contractAddress);
716
- const currentMessageSender = AztecAddress.fromField(this.msgSender);
717
- const currentFunctionSelector = FunctionSelector.fromField(this.functionSelector.toField());
718
- this.setMsgSender(this.contractAddress);
719
- this.setContractAddress(targetContractAddress);
720
- this.setFunctionSelector(functionSelector);
721
- this.setCalldata(args);
722
-
723
- const callContext = CallContext.empty();
724
- callContext.msgSender = this.msgSender;
725
- callContext.functionSelector = this.functionSelector;
726
- callContext.storageContractAddress = targetContractAddress;
727
- callContext.isStaticCall = isStaticCall;
728
- callContext.isDelegateCall = isDelegateCall;
729
-
730
- const executionResult = await this.executePublicFunction(
731
- targetContractAddress,
732
- args,
733
- callContext,
734
- this.sideEffectsCounter,
735
- );
736
-
737
- // Apply side effects
738
- if (!executionResult.reverted) {
739
- this.sideEffectsCounter = executionResult.endSideEffectCounter.toNumber() + 1;
740
- }
741
- this.setContractAddress(currentContractAddress);
742
- this.setMsgSender(currentMessageSender);
743
- this.setFunctionSelector(currentFunctionSelector);
744
-
745
- return executionResult;
746
- }
747
-
748
659
  async enqueuePublicFunctionCall(
749
660
  targetContractAddress: AztecAddress,
750
661
  functionSelector: FunctionSelector,
751
662
  argsHash: Fr,
752
663
  sideEffectCounter: number,
753
664
  isStaticCall: boolean,
754
- isDelegateCall: boolean,
755
665
  ): Promise<Fr> {
756
666
  // Store and modify env
757
667
  const currentContractAddress = AztecAddress.fromField(this.contractAddress);
@@ -761,29 +671,44 @@ export class TXE implements TypedOracle {
761
671
  this.setContractAddress(targetContractAddress);
762
672
  this.setFunctionSelector(functionSelector);
763
673
 
764
- const callContext = CallContext.empty();
765
- callContext.msgSender = this.msgSender;
766
- callContext.functionSelector = FunctionSelector.fromField(new Fr(PUBLIC_DISPATCH_SELECTOR));
767
- callContext.storageContractAddress = targetContractAddress;
768
- callContext.isStaticCall = isStaticCall;
769
- callContext.isDelegateCall = isDelegateCall;
674
+ const callContext = new CallContext(
675
+ /* msgSender */ currentContractAddress,
676
+ targetContractAddress,
677
+ FunctionSelector.fromField(new Fr(PUBLIC_DISPATCH_SELECTOR)),
678
+ isStaticCall,
679
+ );
770
680
 
771
681
  const args = [this.functionSelector.toField(), ...this.packedValuesCache.unpack(argsHash)];
772
682
  const newArgsHash = this.packedValuesCache.pack(args);
773
683
 
774
- const executionResult = await this.executePublicFunction(
775
- targetContractAddress,
776
- args,
777
- callContext,
778
- sideEffectCounter,
779
- );
684
+ const executionResult = await this.executePublicFunction(args, callContext, sideEffectCounter);
780
685
 
686
+ // Poor man's revert handling
781
687
  if (executionResult.reverted) {
782
- throw new Error(`Execution reverted with reason: ${executionResult.revertReason}`);
688
+ if (executionResult.revertReason && executionResult.revertReason instanceof SimulationError) {
689
+ await enrichPublicSimulationError(
690
+ executionResult.revertReason,
691
+ this.contractDataOracle,
692
+ this.txeDatabase,
693
+ this.logger,
694
+ );
695
+ throw new Error(executionResult.revertReason.message);
696
+ } else {
697
+ throw new Error(`Enqueued public function call reverted: ${executionResult.revertReason}`);
698
+ }
783
699
  }
784
700
 
785
701
  // Apply side effects
786
702
  this.sideEffectsCounter = executionResult.endSideEffectCounter.toNumber() + 1;
703
+ await this.addNoteHashes(
704
+ targetContractAddress,
705
+ executionResult.noteHashes.map(noteHash => noteHash.value),
706
+ );
707
+ await this.addNullifiers(
708
+ targetContractAddress,
709
+ executionResult.nullifiers.map(nullifier => nullifier.value),
710
+ );
711
+
787
712
  this.setContractAddress(currentContractAddress);
788
713
  this.setMsgSender(currentMessageSender);
789
714
  this.setFunctionSelector(currentFunctionSelector);
@@ -797,7 +722,6 @@ export class TXE implements TypedOracle {
797
722
  argsHash: Fr,
798
723
  sideEffectCounter: number,
799
724
  isStaticCall: boolean,
800
- isDelegateCall: boolean,
801
725
  ): Promise<Fr> {
802
726
  // Definitely not right, in that the teardown should always be last.
803
727
  // But useful for executing flows.
@@ -807,7 +731,6 @@ export class TXE implements TypedOracle {
807
731
  argsHash,
808
732
  sideEffectCounter,
809
733
  isStaticCall,
810
- isDelegateCall,
811
734
  );
812
735
  }
813
736
 
@@ -828,4 +751,117 @@ export class TXE implements TypedOracle {
828
751
  this.sideEffectsCounter = counter + 1;
829
752
  return;
830
753
  }
754
+
755
+ async getAppTaggingSecret(sender: AztecAddress, recipient: AztecAddress): Promise<IndexedTaggingSecret> {
756
+ const senderCompleteAddress = await this.getCompleteAddress(sender);
757
+ const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
758
+ const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient);
759
+ // Silo the secret to the app so it can't be used to track other app's notes
760
+ const secret = poseidon2Hash([sharedSecret.x, sharedSecret.y, this.contractAddress]);
761
+ const [index] = await this.txeDatabase.getTaggingSecretsIndexes([secret]);
762
+ return new IndexedTaggingSecret(secret, index);
763
+ }
764
+
765
+ async getAppTaggingSecretsForSenders(recipient: AztecAddress): Promise<IndexedTaggingSecret[]> {
766
+ const recipientCompleteAddress = await this.getCompleteAddress(recipient);
767
+ const completeAddresses = await this.txeDatabase.getCompleteAddresses();
768
+ // Filter out the addresses corresponding to accounts
769
+ const accounts = await this.keyStore.getAccounts();
770
+ const senders = completeAddresses.filter(
771
+ completeAddress => !accounts.find(account => account.equals(completeAddress.address)),
772
+ );
773
+ const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
774
+ const secrets = senders.map(({ address: sender }) => {
775
+ const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, sender);
776
+ return poseidon2Hash([sharedSecret.x, sharedSecret.y, this.contractAddress]);
777
+ });
778
+ const indexes = await this.txeDatabase.getTaggingSecretsIndexes(secrets);
779
+ return secrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
780
+ }
781
+
782
+ // AVM oracles
783
+
784
+ async avmOpcodeCall(targetContractAddress: AztecAddress, args: Fr[], isStaticCall: boolean) {
785
+ // Store and modify env
786
+ const currentContractAddress = AztecAddress.fromField(this.contractAddress);
787
+ const currentMessageSender = AztecAddress.fromField(this.msgSender);
788
+ this.setMsgSender(this.contractAddress);
789
+ this.setContractAddress(targetContractAddress);
790
+
791
+ const callContext = new CallContext(
792
+ /* msgSender */ currentContractAddress,
793
+ targetContractAddress,
794
+ FunctionSelector.fromField(new Fr(PUBLIC_DISPATCH_SELECTOR)),
795
+ isStaticCall,
796
+ );
797
+
798
+ const executionResult = await this.executePublicFunction(args, callContext, this.sideEffectsCounter);
799
+ // Save return/revert data for later.
800
+ this.nestedCallReturndata = executionResult.returnValues;
801
+
802
+ // Apply side effects
803
+ if (!executionResult.reverted) {
804
+ this.sideEffectsCounter = executionResult.endSideEffectCounter.toNumber() + 1;
805
+ await this.addNoteHashes(
806
+ targetContractAddress,
807
+ executionResult.noteHashes.map(noteHash => noteHash.value),
808
+ );
809
+ await this.addNullifiers(
810
+ targetContractAddress,
811
+ executionResult.nullifiers.map(nullifier => nullifier.value),
812
+ );
813
+ }
814
+
815
+ this.setContractAddress(currentContractAddress);
816
+ this.setMsgSender(currentMessageSender);
817
+
818
+ return executionResult;
819
+ }
820
+
821
+ avmOpcodeReturndataSize(): number {
822
+ return this.nestedCallReturndata.length;
823
+ }
824
+
825
+ avmOpcodeReturndataCopy(rdOffset: number, copySize: number): Fr[] {
826
+ return this.nestedCallReturndata.slice(rdOffset, rdOffset + copySize);
827
+ }
828
+
829
+ async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean> {
830
+ const nullifier = siloNullifier(targetAddress, innerNullifier!);
831
+ const db = await this.trees.getLatest();
832
+ const index = await db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer());
833
+ return index !== undefined;
834
+ }
835
+
836
+ async avmOpcodeEmitNullifier(nullifier: Fr) {
837
+ const db = await this.trees.getLatest();
838
+ const siloedNullifier = siloNullifier(this.contractAddress, nullifier);
839
+ await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT);
840
+ return Promise.resolve();
841
+ }
842
+
843
+ async avmOpcodeEmitNoteHash(noteHash: Fr) {
844
+ const db = await this.trees.getLatest();
845
+ const siloedNoteHash = siloNoteHash(this.contractAddress, noteHash);
846
+ await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [siloedNoteHash]);
847
+ return Promise.resolve();
848
+ }
849
+
850
+ async avmOpcodeStorageRead(slot: Fr) {
851
+ const db = await this.trees.getLatest();
852
+
853
+ const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, slot);
854
+
855
+ const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
856
+ if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
857
+ return Fr.ZERO;
858
+ }
859
+
860
+ const preimage = (await db.getLeafPreimage(
861
+ MerkleTreeId.PUBLIC_DATA_TREE,
862
+ lowLeafResult.index,
863
+ )) as PublicDataTreeLeafPreimage;
864
+
865
+ return preimage.value;
866
+ }
831
867
  }