@aztec/sequencer-client 0.31.0 → 0.32.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.
Files changed (31) hide show
  1. package/dest/client/sequencer-client.d.ts.map +1 -1
  2. package/dest/client/sequencer-client.js +4 -3
  3. package/dest/config.d.ts.map +1 -1
  4. package/dest/config.js +7 -3
  5. package/dest/sequencer/abstract_phase_manager.d.ts +2 -2
  6. package/dest/sequencer/abstract_phase_manager.d.ts.map +1 -1
  7. package/dest/sequencer/abstract_phase_manager.js +1 -1
  8. package/dest/sequencer/public_processor.d.ts +2 -3
  9. package/dest/sequencer/public_processor.d.ts.map +1 -1
  10. package/dest/sequencer/public_processor.js +5 -5
  11. package/dest/sequencer/sequencer.d.ts +5 -2
  12. package/dest/sequencer/sequencer.d.ts.map +1 -1
  13. package/dest/sequencer/sequencer.js +13 -13
  14. package/dest/sequencer/tx_validator.d.ts +8 -1
  15. package/dest/sequencer/tx_validator.d.ts.map +1 -1
  16. package/dest/sequencer/tx_validator.js +58 -23
  17. package/dest/sequencer/tx_validator_factory.d.ts +12 -0
  18. package/dest/sequencer/tx_validator_factory.d.ts.map +1 -0
  19. package/dest/sequencer/tx_validator_factory.js +17 -0
  20. package/dest/simulator/public_executor.d.ts +2 -3
  21. package/dest/simulator/public_executor.d.ts.map +1 -1
  22. package/dest/simulator/public_executor.js +7 -8
  23. package/package.json +13 -13
  24. package/src/client/sequencer-client.ts +3 -7
  25. package/src/config.ts +7 -1
  26. package/src/sequencer/abstract_phase_manager.ts +3 -3
  27. package/src/sequencer/public_processor.ts +3 -3
  28. package/src/sequencer/sequencer.ts +14 -13
  29. package/src/sequencer/tx_validator.ts +79 -26
  30. package/src/sequencer/tx_validator_factory.ts +32 -0
  31. package/src/simulator/public_executor.ts +6 -16
@@ -1,8 +1,9 @@
1
1
  import { ProcessedTx, Tx } from '@aztec/circuit-types';
2
- import { AztecAddress, EthAddress, Fr, GlobalVariables } from '@aztec/circuits.js';
2
+ import { AztecAddress, EthAddress, Fr, GlobalVariables, PublicCallRequest } from '@aztec/circuits.js';
3
3
  import { pedersenHash } from '@aztec/foundation/crypto';
4
4
  import { Logger, createDebugLogger } from '@aztec/foundation/log';
5
5
  import { getCanonicalGasTokenAddress } from '@aztec/protocol-contracts/gas-token';
6
+ import { ContractDataSource } from '@aztec/types/contracts';
6
7
 
7
8
  import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
8
9
 
@@ -27,25 +28,33 @@ type TxValidationStatus = typeof VALID_TX | typeof INVALID_TX;
27
28
  // the storage slot associated with "storage.balances"
28
29
  const GAS_TOKEN_BALANCES_SLOT = new Fr(1);
29
30
 
31
+ type FeeValidationConfig = {
32
+ gasPortalAddress: EthAddress;
33
+ allowedFeePaymentContractClasses: Fr[];
34
+ allowedFeePaymentContractInstances: AztecAddress[];
35
+ };
36
+
30
37
  export class TxValidator {
31
38
  #log: Logger;
32
39
  #globalVariables: GlobalVariables;
33
40
  #nullifierSource: NullifierSource;
34
41
  #publicStateSource: PublicStateSource;
35
- #gasPortalAddress: EthAddress;
42
+ #contractDataSource: ContractDataSource;
43
+ #feeValidationConfig: FeeValidationConfig;
36
44
 
37
45
  constructor(
38
46
  nullifierSource: NullifierSource,
39
47
  publicStateSource: PublicStateSource,
40
- gasPortalAddress: EthAddress,
48
+ contractDataSource: ContractDataSource,
41
49
  globalVariables: GlobalVariables,
50
+ feeValidationConfig: FeeValidationConfig,
42
51
  log = createDebugLogger('aztec:sequencer:tx_validator'),
43
52
  ) {
44
53
  this.#nullifierSource = nullifierSource;
45
- this.#globalVariables = globalVariables;
46
54
  this.#publicStateSource = publicStateSource;
47
- this.#gasPortalAddress = gasPortalAddress;
48
-
55
+ this.#contractDataSource = contractDataSource;
56
+ this.#globalVariables = globalVariables;
57
+ this.#feeValidationConfig = feeValidationConfig;
49
58
  this.#log = log;
50
59
  }
51
60
 
@@ -71,9 +80,15 @@ export class TxValidator {
71
80
  }
72
81
 
73
82
  // skip already processed transactions
74
- if (tx instanceof Tx && (await this.#validateFee(tx)) === INVALID_TX) {
75
- invalidTxs.push(tx);
76
- continue;
83
+ if (tx instanceof Tx) {
84
+ if ((await this.#validateFee(tx)) === INVALID_TX) {
85
+ invalidTxs.push(tx);
86
+ continue;
87
+ }
88
+ if ((await this.#validateGasBalance(tx)) === INVALID_TX) {
89
+ invalidTxs.push(tx);
90
+ continue;
91
+ }
77
92
  }
78
93
 
79
94
  if (this.#validateMaxBlockNumber(tx) === INVALID_TX) {
@@ -150,30 +165,17 @@ export class TxValidator {
150
165
  return VALID_TX;
151
166
  }
152
167
 
153
- async #validateFee(tx: Tx): Promise<TxValidationStatus> {
168
+ async #validateGasBalance(tx: Tx): Promise<TxValidationStatus> {
154
169
  if (!tx.data.needsTeardown) {
155
- // TODO check if fees are mandatory and reject this tx
156
- this.#log.debug(`Tx ${Tx.getHash(tx)} doesn't pay for gas`);
157
170
  return VALID_TX;
158
171
  }
159
172
 
160
- const {
161
- // TODO what if there's more than one function call?
162
- // if we're to enshrine that teardown = 1 function call, then we should turn this into a single function call
163
- [PublicKernelPhase.TEARDOWN]: [teardownFn],
164
- } = AbstractPhaseManager.extractEnqueuedPublicCallsByPhase(tx.data, tx.enqueuedPublicFunctionCalls);
165
-
166
- if (!teardownFn) {
167
- this.#log.warn(
168
- `Rejecting tx ${Tx.getHash(tx)} because it should pay for gas but has no enqueued teardown function call`,
169
- );
170
- return INVALID_TX;
171
- }
173
+ const teardownFn = TxValidator.#extractFeeExecutionCall(tx)!;
172
174
 
173
175
  // TODO(#1204) if a generator index is used for the derived storage slot of a map, update it here as well
174
- const slot = pedersenHash([GAS_TOKEN_BALANCES_SLOT.toBuffer(), teardownFn.callContext.msgSender.toBuffer()]);
176
+ const slot = pedersenHash([GAS_TOKEN_BALANCES_SLOT, teardownFn.callContext.msgSender]);
175
177
  const gasBalance = await this.#publicStateSource.storageRead(
176
- getCanonicalGasTokenAddress(this.#gasPortalAddress),
178
+ getCanonicalGasTokenAddress(this.#feeValidationConfig.gasPortalAddress),
177
179
  slot,
178
180
  );
179
181
 
@@ -201,4 +203,55 @@ export class TxValidator {
201
203
  return VALID_TX;
202
204
  }
203
205
  }
206
+
207
+ async #validateFee(tx: Tx): Promise<TxValidationStatus> {
208
+ if (!tx.data.needsTeardown) {
209
+ // TODO check if fees are mandatory and reject this tx
210
+ this.#log.debug(`Tx ${Tx.getHash(tx)} doesn't pay for gas`);
211
+ return VALID_TX;
212
+ }
213
+
214
+ const teardownFn = TxValidator.#extractFeeExecutionCall(tx);
215
+ if (!teardownFn) {
216
+ this.#log.warn(
217
+ `Rejecting tx ${Tx.getHash(tx)} because it should pay for gas but has no enqueued teardown function call`,
218
+ );
219
+ return INVALID_TX;
220
+ }
221
+
222
+ const fpcAddress = teardownFn.contractAddress;
223
+ const contractClass = await this.#contractDataSource.getContract(fpcAddress);
224
+
225
+ if (!contractClass) {
226
+ return INVALID_TX;
227
+ }
228
+
229
+ if (fpcAddress.equals(getCanonicalGasTokenAddress(this.#feeValidationConfig.gasPortalAddress))) {
230
+ return VALID_TX;
231
+ }
232
+
233
+ for (const allowedContract of this.#feeValidationConfig.allowedFeePaymentContractInstances) {
234
+ if (fpcAddress.equals(allowedContract)) {
235
+ return VALID_TX;
236
+ }
237
+ }
238
+
239
+ for (const allowedContractClass of this.#feeValidationConfig.allowedFeePaymentContractClasses) {
240
+ if (contractClass.contractClassId.equals(allowedContractClass)) {
241
+ return VALID_TX;
242
+ }
243
+ }
244
+
245
+ return INVALID_TX;
246
+ }
247
+
248
+ static #extractFeeExecutionCall(tx: Tx): PublicCallRequest | undefined {
249
+ const {
250
+ // TODO what if there's more than one function call?
251
+ // if we're to enshrine that teardown = 1 function call, then we should turn this into a single function call
252
+ [PublicKernelPhase.TEARDOWN]: [teardownFn],
253
+ } = AbstractPhaseManager.extractEnqueuedPublicCallsByPhase(tx.data, tx.enqueuedPublicFunctionCalls);
254
+
255
+ return teardownFn;
256
+ }
204
257
  }
@@ -0,0 +1,32 @@
1
+ import { AztecAddress, EthAddress, Fr, GlobalVariables } from '@aztec/circuits.js';
2
+ import { ContractDataSource } from '@aztec/types/contracts';
3
+ import { MerkleTreeOperations } from '@aztec/world-state';
4
+
5
+ import { WorldStateDB, WorldStatePublicDB } from '../simulator/public_executor.js';
6
+ import { TxValidator } from './tx_validator.js';
7
+
8
+ export class TxValidatorFactory {
9
+ constructor(
10
+ private merkleTreeDb: MerkleTreeOperations,
11
+ private contractDataSource: ContractDataSource,
12
+ private gasPortalAddress: EthAddress,
13
+ ) {}
14
+
15
+ buildTxValidator(
16
+ globalVariables: GlobalVariables,
17
+ allowedFeePaymentContractClasses: Fr[],
18
+ allowedFeePaymentContractInstances: AztecAddress[],
19
+ ): TxValidator {
20
+ return new TxValidator(
21
+ new WorldStateDB(this.merkleTreeDb),
22
+ new WorldStatePublicDB(this.merkleTreeDb),
23
+ this.contractDataSource,
24
+ globalVariables,
25
+ {
26
+ allowedFeePaymentContractClasses,
27
+ allowedFeePaymentContractInstances,
28
+ gasPortalAddress: this.gasPortalAddress,
29
+ },
30
+ );
31
+ }
32
+ }
@@ -1,10 +1,4 @@
1
- import {
2
- L1ToL2MessageSource,
3
- MerkleTreeId,
4
- NullifierMembershipWitness,
5
- Tx,
6
- UnencryptedL2Log,
7
- } from '@aztec/circuit-types';
1
+ import { MerkleTreeId, NullifierMembershipWitness, Tx } from '@aztec/circuit-types';
8
2
  import {
9
3
  AztecAddress,
10
4
  ContractClassRegisteredEvent,
@@ -42,7 +36,7 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB {
42
36
  */
43
37
  public addNewContracts(tx: Tx): Promise<void> {
44
38
  // Extract contract class and instance data from logs and add to cache for this block
45
- const logs = tx.unencryptedLogs.unrollLogs().map(UnencryptedL2Log.fromBuffer);
39
+ const logs = tx.unencryptedLogs.unrollLogs();
46
40
  ContractClassRegisteredEvent.fromLogs(logs, getCanonicalClassRegistererAddress()).forEach(e => {
47
41
  this.log(`Adding class ${e.contractClassId.toString()} to public execution contract cache`);
48
42
  this.classCache.set(e.contractClassId.toString(), e.toContractClassPublic());
@@ -65,7 +59,7 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB {
65
59
  // TODO(@spalladino): Can this inadvertently delete a valid contract added by another tx?
66
60
  // Let's say we have two txs adding the same contract on the same block. If the 2nd one reverts,
67
61
  // wouldn't that accidentally remove the contract added on the first one?
68
- const logs = tx.unencryptedLogs.unrollLogs().map(UnencryptedL2Log.fromBuffer);
62
+ const logs = tx.unencryptedLogs.unrollLogs();
69
63
  ContractClassRegisteredEvent.fromLogs(logs, getCanonicalClassRegistererAddress()).forEach(e =>
70
64
  this.classCache.delete(e.contractClassId.toString()),
71
65
  );
@@ -198,7 +192,7 @@ export class WorldStatePublicDB implements PublicStateDB {
198
192
  * Implements WorldState db using a world state database.
199
193
  */
200
194
  export class WorldStateDB implements CommitmentsDB {
201
- constructor(private db: MerkleTreeOperations, private l1ToL2MessageSource: L1ToL2MessageSource) {}
195
+ constructor(private db: MerkleTreeOperations) {}
202
196
 
203
197
  public async getNullifierMembershipWitnessAtLatestBlock(
204
198
  nullifier: Fr,
@@ -235,11 +229,7 @@ export class WorldStateDB implements CommitmentsDB {
235
229
  // We iterate over messages until we find one whose nullifier is not in the nullifier tree --> we need to check
236
230
  // for nullifiers because messages can have duplicates.
237
231
  do {
238
- messageIndex = (await this.db.findLeafIndexAfter(
239
- MerkleTreeId.L1_TO_L2_MESSAGE_TREE,
240
- messageHash.toBuffer(),
241
- startIndex,
242
- ))!;
232
+ messageIndex = (await this.db.findLeafIndexAfter(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, messageHash, startIndex))!;
243
233
  if (messageIndex === undefined) {
244
234
  throw new Error(`No non-nullified L1 to L2 message found for message hash ${messageHash.toString()}`);
245
235
  }
@@ -259,7 +249,7 @@ export class WorldStateDB implements CommitmentsDB {
259
249
  }
260
250
 
261
251
  public async getCommitmentIndex(commitment: Fr): Promise<bigint | undefined> {
262
- return await this.db.findLeafIndex(MerkleTreeId.NOTE_HASH_TREE, commitment.toBuffer());
252
+ return await this.db.findLeafIndex(MerkleTreeId.NOTE_HASH_TREE, commitment);
263
253
  }
264
254
 
265
255
  public async getNullifierIndex(nullifier: Fr): Promise<bigint | undefined> {