@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.
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +4 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +7 -3
- package/dest/sequencer/abstract_phase_manager.d.ts +2 -2
- package/dest/sequencer/abstract_phase_manager.d.ts.map +1 -1
- package/dest/sequencer/abstract_phase_manager.js +1 -1
- package/dest/sequencer/public_processor.d.ts +2 -3
- package/dest/sequencer/public_processor.d.ts.map +1 -1
- package/dest/sequencer/public_processor.js +5 -5
- package/dest/sequencer/sequencer.d.ts +5 -2
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +13 -13
- package/dest/sequencer/tx_validator.d.ts +8 -1
- package/dest/sequencer/tx_validator.d.ts.map +1 -1
- package/dest/sequencer/tx_validator.js +58 -23
- package/dest/sequencer/tx_validator_factory.d.ts +12 -0
- package/dest/sequencer/tx_validator_factory.d.ts.map +1 -0
- package/dest/sequencer/tx_validator_factory.js +17 -0
- package/dest/simulator/public_executor.d.ts +2 -3
- package/dest/simulator/public_executor.d.ts.map +1 -1
- package/dest/simulator/public_executor.js +7 -8
- package/package.json +13 -13
- package/src/client/sequencer-client.ts +3 -7
- package/src/config.ts +7 -1
- package/src/sequencer/abstract_phase_manager.ts +3 -3
- package/src/sequencer/public_processor.ts +3 -3
- package/src/sequencer/sequencer.ts +14 -13
- package/src/sequencer/tx_validator.ts +79 -26
- package/src/sequencer/tx_validator_factory.ts +32 -0
- 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
|
-
#
|
|
42
|
+
#contractDataSource: ContractDataSource;
|
|
43
|
+
#feeValidationConfig: FeeValidationConfig;
|
|
36
44
|
|
|
37
45
|
constructor(
|
|
38
46
|
nullifierSource: NullifierSource,
|
|
39
47
|
publicStateSource: PublicStateSource,
|
|
40
|
-
|
|
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.#
|
|
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
|
|
75
|
-
|
|
76
|
-
|
|
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 #
|
|
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
|
|
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()
|
|
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()
|
|
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
|
|
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
|
|
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> {
|