@aztec/sequencer-client 0.69.0-devnet → 0.69.1
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 +2 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -1
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +3 -2
- package/dest/publisher/config.d.ts +4 -0
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +6 -1
- package/dest/publisher/l1-publisher.d.ts +13 -0
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +60 -50
- package/dest/sequencer/index.d.ts +1 -0
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +2 -1
- package/dest/sequencer/sequencer.d.ts +7 -16
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +53 -128
- package/dest/sequencer/utils.d.ts +2 -2
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +3 -3
- package/dest/tx_validator/gas_validator.d.ts +2 -3
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +9 -22
- package/dest/tx_validator/nullifier_cache.d.ts +16 -0
- package/dest/tx_validator/nullifier_cache.d.ts.map +1 -0
- package/dest/tx_validator/nullifier_cache.js +24 -0
- package/dest/tx_validator/phases_validator.d.ts +2 -3
- package/dest/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/tx_validator/phases_validator.js +15 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +15 -14
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +38 -24
- package/package.json +20 -19
- package/src/client/sequencer-client.ts +1 -2
- package/src/config.ts +10 -0
- package/src/index.ts +2 -1
- package/src/publisher/config.ts +10 -0
- package/src/publisher/l1-publisher.ts +70 -46
- package/src/sequencer/index.ts +1 -0
- package/src/sequencer/sequencer.ts +60 -177
- package/src/sequencer/utils.ts +2 -2
- package/src/tx_validator/gas_validator.ts +11 -24
- package/src/tx_validator/nullifier_cache.ts +29 -0
- package/src/tx_validator/phases_validator.ts +22 -33
- package/src/tx_validator/tx_validator_factory.ts +82 -40
package/src/sequencer/utils.ts
CHANGED
|
@@ -19,9 +19,9 @@ export enum SequencerState {
|
|
|
19
19
|
*/
|
|
20
20
|
PROPOSER_CHECK = 'PROPOSER_CHECK',
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Initializing the block proposal. Will move to CREATING_BLOCK if there are valid txs to include, or back to SYNCHRONIZING otherwise.
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
INITIALIZING_PROPOSAL = 'INITIALIZING_PROPOSAL',
|
|
25
25
|
/**
|
|
26
26
|
* Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA.
|
|
27
27
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Tx, TxExecutionPhase, type TxValidator } from '@aztec/circuit-types';
|
|
1
|
+
import { type Tx, TxExecutionPhase, type TxValidationResult, type TxValidator } from '@aztec/circuit-types';
|
|
2
2
|
import { type AztecAddress, type Fr, FunctionSelector, type GasFees } from '@aztec/circuits.js';
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { computeFeePayerBalanceStorageSlot, getExecutionRequestsByPhase } from '@aztec/simulator';
|
|
@@ -27,25 +27,10 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
27
27
|
this.#gasFees = gasFees;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const skippedTxs: Tx[] = [];
|
|
34
|
-
|
|
35
|
-
for (const tx of txs) {
|
|
36
|
-
if (this.#shouldSkip(tx)) {
|
|
37
|
-
skippedTxs.push(tx);
|
|
38
|
-
} else if (await this.#validateTxFee(tx)) {
|
|
39
|
-
validTxs.push(tx);
|
|
40
|
-
} else {
|
|
41
|
-
invalidTxs.push(tx);
|
|
42
|
-
}
|
|
30
|
+
validateTx(tx: Tx): Promise<TxValidationResult> {
|
|
31
|
+
if (this.#shouldSkip(tx)) {
|
|
32
|
+
return Promise.resolve({ result: 'skipped', reason: ['Insufficient fee per gas'] });
|
|
43
33
|
}
|
|
44
|
-
|
|
45
|
-
return [validTxs, invalidTxs, skippedTxs];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
validateTx(tx: Tx): Promise<boolean> {
|
|
49
34
|
return this.#validateTxFee(tx);
|
|
50
35
|
}
|
|
51
36
|
|
|
@@ -57,20 +42,22 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
57
42
|
const notEnoughMaxFees =
|
|
58
43
|
maxFeesPerGas.feePerDaGas.lt(this.#gasFees.feePerDaGas) ||
|
|
59
44
|
maxFeesPerGas.feePerL2Gas.lt(this.#gasFees.feePerL2Gas);
|
|
45
|
+
|
|
60
46
|
if (notEnoughMaxFees) {
|
|
61
47
|
this.#log.warn(`Skipping transaction ${tx.getTxHash()} due to insufficient fee per gas`);
|
|
62
48
|
}
|
|
63
49
|
return notEnoughMaxFees;
|
|
64
50
|
}
|
|
65
51
|
|
|
66
|
-
async #validateTxFee(tx: Tx): Promise<
|
|
52
|
+
async #validateTxFee(tx: Tx): Promise<TxValidationResult> {
|
|
67
53
|
const feePayer = tx.data.feePayer;
|
|
68
54
|
// TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx
|
|
69
55
|
if (feePayer.isZero()) {
|
|
70
56
|
if (this.#enforceFees) {
|
|
71
57
|
this.#log.warn(`Rejecting transaction ${tx.getTxHash()} due to missing fee payer`);
|
|
58
|
+
return { result: 'invalid', reason: ['Missing fee payer'] };
|
|
72
59
|
} else {
|
|
73
|
-
return
|
|
60
|
+
return { result: 'valid' };
|
|
74
61
|
}
|
|
75
62
|
}
|
|
76
63
|
|
|
@@ -98,13 +85,13 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
98
85
|
|
|
99
86
|
const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[2]) : initialBalance;
|
|
100
87
|
if (balance.lt(feeLimit)) {
|
|
101
|
-
this.#log.
|
|
88
|
+
this.#log.warn(`Rejecting transaction due to not enough fee payer balance`, {
|
|
102
89
|
feePayer,
|
|
103
90
|
balance: balance.toBigInt(),
|
|
104
91
|
feeLimit: feeLimit.toBigInt(),
|
|
105
92
|
});
|
|
106
|
-
return
|
|
93
|
+
return { result: 'invalid', reason: ['Insufficient fee payer balance'] };
|
|
107
94
|
}
|
|
108
|
-
return
|
|
95
|
+
return { result: 'valid' };
|
|
109
96
|
}
|
|
110
97
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MerkleTreeId, type MerkleTreeReadOperations } from '@aztec/circuit-types';
|
|
2
|
+
import { type NullifierSource } from '@aztec/p2p';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Implements a nullifier source by checking a DB and an in-memory collection.
|
|
6
|
+
* Intended for validating transactions as they are added to a block.
|
|
7
|
+
*/
|
|
8
|
+
export class NullifierCache implements NullifierSource {
|
|
9
|
+
nullifiers: Set<string>;
|
|
10
|
+
|
|
11
|
+
constructor(private db: MerkleTreeReadOperations) {
|
|
12
|
+
this.nullifiers = new Set();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async nullifiersExist(nullifiers: Buffer[]): Promise<boolean[]> {
|
|
16
|
+
const cacheResults = nullifiers.map(n => this.nullifiers.has(n.toString()));
|
|
17
|
+
const toCheckDb = nullifiers.filter((_n, index) => !cacheResults[index]);
|
|
18
|
+
const dbHits = await this.db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, toCheckDb);
|
|
19
|
+
|
|
20
|
+
let dbIndex = 0;
|
|
21
|
+
return nullifiers.map((_n, index) => cacheResults[index] || dbHits[dbIndex++] !== undefined);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public addNullifiers(nullifiers: Buffer[]) {
|
|
25
|
+
for (const nullifier of nullifiers) {
|
|
26
|
+
this.nullifiers.add(nullifier.toString());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type PublicExecutionRequest,
|
|
4
4
|
Tx,
|
|
5
5
|
TxExecutionPhase,
|
|
6
|
+
type TxValidationResult,
|
|
6
7
|
type TxValidator,
|
|
7
8
|
} from '@aztec/circuit-types';
|
|
8
9
|
import { type ContractDataSource } from '@aztec/circuits.js';
|
|
@@ -17,48 +18,36 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
17
18
|
this.contractDataSource = new ContractsDataSourcePublicDB(contracts);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
async
|
|
21
|
-
|
|
22
|
-
const invalidTxs: Tx[] = [];
|
|
23
|
-
|
|
24
|
-
for (const tx of txs) {
|
|
21
|
+
async validateTx(tx: Tx): Promise<TxValidationResult> {
|
|
22
|
+
try {
|
|
25
23
|
// TODO(@spalladino): We add this just to handle public authwit-check calls during setup
|
|
26
24
|
// which are needed for public FPC flows, but fail if the account contract hasnt been deployed yet,
|
|
27
25
|
// which is what we're trying to do as part of the current txs.
|
|
28
26
|
await this.contractDataSource.addNewContracts(tx);
|
|
29
27
|
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
invalidTxs.push(tx);
|
|
28
|
+
if (!tx.data.forPublic) {
|
|
29
|
+
this.#log.debug(`Tx ${Tx.getHash(tx)} does not contain enqueued public functions. Skipping phases validation.`);
|
|
30
|
+
return { result: 'valid' };
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP);
|
|
49
|
-
for (const setupFn of setupFns) {
|
|
50
|
-
if (!(await this.isOnAllowList(setupFn, this.setupAllowList))) {
|
|
51
|
-
this.#log.warn(
|
|
52
|
-
`Rejecting tx ${Tx.getHash(tx)} because it calls setup function not on allow list: ${
|
|
53
|
-
setupFn.callContext.contractAddress
|
|
54
|
-
}:${setupFn.callContext.functionSelector}`,
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
return false;
|
|
33
|
+
const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP);
|
|
34
|
+
for (const setupFn of setupFns) {
|
|
35
|
+
if (!(await this.isOnAllowList(setupFn, this.setupAllowList))) {
|
|
36
|
+
this.#log.warn(
|
|
37
|
+
`Rejecting tx ${Tx.getHash(tx)} because it calls setup function not on allow list: ${
|
|
38
|
+
setupFn.callContext.contractAddress
|
|
39
|
+
}:${setupFn.callContext.functionSelector}`,
|
|
40
|
+
{ allowList: this.setupAllowList },
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return { result: 'invalid', reason: ['Setup function not on allow list'] };
|
|
44
|
+
}
|
|
58
45
|
}
|
|
59
|
-
}
|
|
60
46
|
|
|
61
|
-
|
|
47
|
+
return { result: 'valid' };
|
|
48
|
+
} finally {
|
|
49
|
+
await this.contractDataSource.removeNewContracts(tx);
|
|
50
|
+
}
|
|
62
51
|
}
|
|
63
52
|
|
|
64
53
|
async isOnAllowList(publicCall: PublicExecutionRequest, allowList: AllowedElement[]): Promise<boolean> {
|
|
@@ -1,65 +1,107 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AllowedElement,
|
|
3
|
-
|
|
3
|
+
type ClientProtocolCircuitVerifier,
|
|
4
4
|
type MerkleTreeReadOperations,
|
|
5
5
|
type ProcessedTx,
|
|
6
6
|
type Tx,
|
|
7
7
|
type TxValidator,
|
|
8
8
|
} from '@aztec/circuit-types';
|
|
9
|
-
import { type ContractDataSource, type GlobalVariables } from '@aztec/circuits.js';
|
|
9
|
+
import { type AztecAddress, type ContractDataSource, Fr, type GasFees, type GlobalVariables } from '@aztec/circuits.js';
|
|
10
10
|
import {
|
|
11
11
|
AggregateTxValidator,
|
|
12
12
|
DataTxValidator,
|
|
13
13
|
DoubleSpendTxValidator,
|
|
14
14
|
MetadataTxValidator,
|
|
15
|
-
|
|
15
|
+
TxProofValidator,
|
|
16
16
|
} from '@aztec/p2p';
|
|
17
17
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
18
18
|
import { readPublicState } from '@aztec/simulator';
|
|
19
19
|
|
|
20
20
|
import { GasTxValidator, type PublicStateSource } from './gas_validator.js';
|
|
21
|
+
import { NullifierCache } from './nullifier_cache.js';
|
|
21
22
|
import { PhasesTxValidator } from './phases_validator.js';
|
|
22
23
|
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
export function createValidatorForAcceptingTxs(
|
|
25
|
+
db: MerkleTreeReadOperations,
|
|
26
|
+
contractDataSource: ContractDataSource,
|
|
27
|
+
verifier: ClientProtocolCircuitVerifier | undefined,
|
|
28
|
+
data: {
|
|
29
|
+
blockNumber: number;
|
|
30
|
+
l1ChainId: number;
|
|
31
|
+
enforceFees: boolean;
|
|
32
|
+
setupAllowList: AllowedElement[];
|
|
33
|
+
gasFees: GasFees;
|
|
34
|
+
},
|
|
35
|
+
): TxValidator<Tx> {
|
|
36
|
+
const { blockNumber, l1ChainId, enforceFees, setupAllowList, gasFees } = data;
|
|
37
|
+
const validators: TxValidator<Tx>[] = [
|
|
38
|
+
new DataTxValidator(),
|
|
39
|
+
new MetadataTxValidator(new Fr(l1ChainId), new Fr(blockNumber)),
|
|
40
|
+
new DoubleSpendTxValidator(new NullifierCache(db)),
|
|
41
|
+
new PhasesTxValidator(contractDataSource, setupAllowList),
|
|
42
|
+
new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, enforceFees, gasFees),
|
|
43
|
+
];
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return readPublicState(this.committedDb, contractAddress, slot);
|
|
41
|
-
},
|
|
42
|
-
};
|
|
45
|
+
if (verifier) {
|
|
46
|
+
validators.push(new TxProofValidator(verifier));
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
return new AggregateTxValidator(...validators);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createValidatorsForBlockBuilding(
|
|
53
|
+
db: MerkleTreeReadOperations,
|
|
54
|
+
contractDataSource: ContractDataSource,
|
|
55
|
+
globalVariables: GlobalVariables,
|
|
56
|
+
enforceFees: boolean,
|
|
57
|
+
setupAllowList: AllowedElement[],
|
|
58
|
+
): {
|
|
59
|
+
preprocessValidator: TxValidator<Tx>;
|
|
60
|
+
postprocessValidator: TxValidator<ProcessedTx>;
|
|
61
|
+
nullifierCache: NullifierCache;
|
|
62
|
+
} {
|
|
63
|
+
const nullifierCache = new NullifierCache(db);
|
|
64
|
+
const publicStateSource = new DatabasePublicStateSource(db);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
preprocessValidator: preprocessValidator(
|
|
68
|
+
nullifierCache,
|
|
69
|
+
publicStateSource,
|
|
70
|
+
contractDataSource,
|
|
71
|
+
enforceFees,
|
|
72
|
+
globalVariables,
|
|
73
|
+
setupAllowList,
|
|
74
|
+
),
|
|
75
|
+
postprocessValidator: postprocessValidator(nullifierCache),
|
|
76
|
+
nullifierCache,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
59
79
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
80
|
+
class DatabasePublicStateSource implements PublicStateSource {
|
|
81
|
+
constructor(private db: MerkleTreeReadOperations) {}
|
|
82
|
+
|
|
83
|
+
storageRead(contractAddress: AztecAddress, slot: Fr): Promise<Fr> {
|
|
84
|
+
return readPublicState(this.db, contractAddress, slot);
|
|
64
85
|
}
|
|
65
86
|
}
|
|
87
|
+
|
|
88
|
+
function preprocessValidator(
|
|
89
|
+
nullifierCache: NullifierCache,
|
|
90
|
+
publicStateSource: PublicStateSource,
|
|
91
|
+
contractDataSource: ContractDataSource,
|
|
92
|
+
enforceFees: boolean,
|
|
93
|
+
globalVariables: GlobalVariables,
|
|
94
|
+
setupAllowList: AllowedElement[],
|
|
95
|
+
): TxValidator<Tx> {
|
|
96
|
+
// We don't include the TxProofValidator nor the DataTxValidator here because they are already checked by the time we get to block building.
|
|
97
|
+
return new AggregateTxValidator(
|
|
98
|
+
new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber),
|
|
99
|
+
new DoubleSpendTxValidator(nullifierCache),
|
|
100
|
+
new PhasesTxValidator(contractDataSource, setupAllowList),
|
|
101
|
+
new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, enforceFees, globalVariables.gasFees),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function postprocessValidator(nullifierCache: NullifierCache): TxValidator<ProcessedTx> {
|
|
106
|
+
return new DoubleSpendTxValidator(nullifierCache);
|
|
107
|
+
}
|