@aztec/sequencer-client 0.46.7 → 0.47.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/config.d.ts.map +1 -1
- package/dest/config.js +10 -26
- package/dest/index.d.ts +2 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +3 -1
- package/dest/publisher/config.d.ts +2 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +14 -2
- package/dest/publisher/index.d.ts +1 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/index.js +2 -1
- package/dest/publisher/l1-publisher.d.ts +3 -3
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +4 -4
- package/dest/publisher/viem-tx-sender.d.ts +1 -1
- package/dest/publisher/viem-tx-sender.d.ts.map +1 -1
- package/dest/publisher/viem-tx-sender.js +4 -4
- package/dest/sequencer/sequencer.d.ts +5 -0
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +66 -19
- package/dest/tx_validator/data_validator.d.ts +6 -0
- package/dest/tx_validator/data_validator.d.ts.map +1 -0
- package/dest/tx_validator/data_validator.js +47 -0
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +2 -2
- 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 +4 -4
- package/dest/tx_validator/test_utils.d.ts.map +1 -1
- package/dest/tx_validator/test_utils.js +10 -4
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +3 -2
- package/package.json +15 -15
- package/src/config.ts +11 -39
- package/src/index.ts +2 -0
- package/src/publisher/config.ts +20 -2
- package/src/publisher/index.ts +1 -1
- package/src/publisher/l1-publisher.ts +5 -5
- package/src/publisher/viem-tx-sender.ts +3 -3
- package/src/sequencer/sequencer.ts +77 -17
- package/src/tx_validator/data_validator.ts +61 -0
- package/src/tx_validator/gas_validator.ts +3 -1
- package/src/tx_validator/phases_validator.ts +13 -5
- package/src/tx_validator/test_utils.ts +10 -3
- package/src/tx_validator/tx_validator_factory.ts +2 -0
package/src/config.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { type AllowedElement } from '@aztec/circuit-types';
|
|
2
2
|
import { AztecAddress, Fr, FunctionSelector, getContractClassFromArtifact } from '@aztec/circuits.js';
|
|
3
|
-
import {
|
|
3
|
+
import { getL1ContractAddressesFromEnv } from '@aztec/ethereum';
|
|
4
4
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
5
|
import { FPCContract } from '@aztec/noir-contracts.js/FPC';
|
|
6
6
|
import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token';
|
|
7
7
|
import { AuthRegistryAddress } from '@aztec/protocol-contracts/auth-registry';
|
|
8
8
|
import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token';
|
|
9
9
|
|
|
10
|
-
import { type Hex } from 'viem';
|
|
11
|
-
|
|
12
10
|
import { type GlobalReaderConfig } from './global_variable_builder/index.js';
|
|
13
|
-
import { type PublisherConfig, type TxSenderConfig } from './publisher/config.js';
|
|
11
|
+
import { type PublisherConfig, type TxSenderConfig, getTxSenderConfigFromEnv } from './publisher/config.js';
|
|
14
12
|
import { type SequencerConfig } from './sequencer/config.js';
|
|
15
13
|
|
|
16
14
|
/** Chain configuration. */
|
|
@@ -35,25 +33,17 @@ export type SequencerClientConfig = PublisherConfig &
|
|
|
35
33
|
*/
|
|
36
34
|
export function getConfigEnvVars(): SequencerClientConfig {
|
|
37
35
|
const {
|
|
38
|
-
SEQ_PUBLISHER_PRIVATE_KEY,
|
|
39
|
-
ETHEREUM_HOST,
|
|
40
|
-
L1_CHAIN_ID,
|
|
41
36
|
VERSION,
|
|
42
|
-
SEQ_REQUIRED_CONFIRMATIONS,
|
|
43
37
|
SEQ_PUBLISH_RETRY_INTERVAL_MS,
|
|
44
38
|
SEQ_TX_POLLING_INTERVAL_MS,
|
|
45
39
|
SEQ_MAX_TX_PER_BLOCK,
|
|
46
40
|
SEQ_MIN_TX_PER_BLOCK,
|
|
41
|
+
SEQ_MAX_SECONDS_BETWEEN_BLOCKS,
|
|
42
|
+
SEQ_MIN_SECONDS_BETWEEN_BLOCKS,
|
|
47
43
|
SEQ_ALLOWED_SETUP_FN,
|
|
48
44
|
SEQ_ALLOWED_TEARDOWN_FN,
|
|
49
45
|
SEQ_MAX_BLOCK_SIZE_IN_BYTES,
|
|
50
|
-
|
|
51
|
-
ROLLUP_CONTRACT_ADDRESS,
|
|
52
|
-
REGISTRY_CONTRACT_ADDRESS,
|
|
53
|
-
INBOX_CONTRACT_ADDRESS,
|
|
54
|
-
OUTBOX_CONTRACT_ADDRESS,
|
|
55
|
-
GAS_TOKEN_CONTRACT_ADDRESS,
|
|
56
|
-
GAS_PORTAL_CONTRACT_ADDRESS,
|
|
46
|
+
SEQ_SKIP_SUBMIT_PROOFS,
|
|
57
47
|
COINBASE,
|
|
58
48
|
FEE_RECIPIENT,
|
|
59
49
|
ACVM_WORKING_DIRECTORY,
|
|
@@ -61,37 +51,18 @@ export function getConfigEnvVars(): SequencerClientConfig {
|
|
|
61
51
|
ENFORCE_FEES = '',
|
|
62
52
|
} = process.env;
|
|
63
53
|
|
|
64
|
-
const publisherPrivateKey: Hex = SEQ_PUBLISHER_PRIVATE_KEY
|
|
65
|
-
? `0x${SEQ_PUBLISHER_PRIVATE_KEY.replace('0x', '')}`
|
|
66
|
-
: NULL_KEY;
|
|
67
|
-
// Populate the relevant addresses for use by the sequencer
|
|
68
|
-
const addresses: L1ContractAddresses = {
|
|
69
|
-
availabilityOracleAddress: AVAILABILITY_ORACLE_CONTRACT_ADDRESS
|
|
70
|
-
? EthAddress.fromString(AVAILABILITY_ORACLE_CONTRACT_ADDRESS)
|
|
71
|
-
: EthAddress.ZERO,
|
|
72
|
-
rollupAddress: ROLLUP_CONTRACT_ADDRESS ? EthAddress.fromString(ROLLUP_CONTRACT_ADDRESS) : EthAddress.ZERO,
|
|
73
|
-
registryAddress: REGISTRY_CONTRACT_ADDRESS ? EthAddress.fromString(REGISTRY_CONTRACT_ADDRESS) : EthAddress.ZERO,
|
|
74
|
-
inboxAddress: INBOX_CONTRACT_ADDRESS ? EthAddress.fromString(INBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
|
|
75
|
-
outboxAddress: OUTBOX_CONTRACT_ADDRESS ? EthAddress.fromString(OUTBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
|
|
76
|
-
gasTokenAddress: GAS_TOKEN_CONTRACT_ADDRESS ? EthAddress.fromString(GAS_TOKEN_CONTRACT_ADDRESS) : EthAddress.ZERO,
|
|
77
|
-
gasPortalAddress: GAS_PORTAL_CONTRACT_ADDRESS
|
|
78
|
-
? EthAddress.fromString(GAS_PORTAL_CONTRACT_ADDRESS)
|
|
79
|
-
: EthAddress.ZERO,
|
|
80
|
-
};
|
|
81
|
-
|
|
82
54
|
return {
|
|
83
55
|
enforceFees: ['1', 'true'].includes(ENFORCE_FEES),
|
|
84
|
-
rpcUrl: ETHEREUM_HOST ? ETHEREUM_HOST : '',
|
|
85
|
-
l1ChainId: L1_CHAIN_ID ? +L1_CHAIN_ID : 31337, // 31337 is the default chain id for anvil
|
|
86
56
|
version: VERSION ? +VERSION : 1, // 1 is our default version
|
|
87
|
-
|
|
88
|
-
l1BlockPublishRetryIntervalMS: SEQ_PUBLISH_RETRY_INTERVAL_MS ? +SEQ_PUBLISH_RETRY_INTERVAL_MS : 1_000,
|
|
57
|
+
l1PublishRetryIntervalMS: SEQ_PUBLISH_RETRY_INTERVAL_MS ? +SEQ_PUBLISH_RETRY_INTERVAL_MS : 1_000,
|
|
89
58
|
transactionPollingIntervalMS: SEQ_TX_POLLING_INTERVAL_MS ? +SEQ_TX_POLLING_INTERVAL_MS : 1_000,
|
|
90
59
|
maxBlockSizeInBytes: SEQ_MAX_BLOCK_SIZE_IN_BYTES ? +SEQ_MAX_BLOCK_SIZE_IN_BYTES : undefined,
|
|
91
|
-
l1Contracts:
|
|
92
|
-
publisherPrivateKey,
|
|
60
|
+
l1Contracts: getL1ContractAddressesFromEnv(),
|
|
93
61
|
maxTxsPerBlock: SEQ_MAX_TX_PER_BLOCK ? +SEQ_MAX_TX_PER_BLOCK : 32,
|
|
94
62
|
minTxsPerBlock: SEQ_MIN_TX_PER_BLOCK ? +SEQ_MIN_TX_PER_BLOCK : 1,
|
|
63
|
+
maxSecondsBetweenBlocks: SEQ_MAX_SECONDS_BETWEEN_BLOCKS ? +SEQ_MAX_SECONDS_BETWEEN_BLOCKS : 0,
|
|
64
|
+
minSecondsBetweenBlocks: SEQ_MIN_SECONDS_BETWEEN_BLOCKS ? +SEQ_MIN_SECONDS_BETWEEN_BLOCKS : 0,
|
|
65
|
+
sequencerSkipSubmitProofs: ['1', 'true'].includes(SEQ_SKIP_SUBMIT_PROOFS ?? ''),
|
|
95
66
|
// TODO: undefined should not be allowed for the following 2 values in PROD
|
|
96
67
|
coinbase: COINBASE ? EthAddress.fromString(COINBASE) : undefined,
|
|
97
68
|
feeRecipient: FEE_RECIPIENT ? AztecAddress.fromString(FEE_RECIPIENT) : undefined,
|
|
@@ -103,6 +74,7 @@ export function getConfigEnvVars(): SequencerClientConfig {
|
|
|
103
74
|
allowedInTeardown: SEQ_ALLOWED_TEARDOWN_FN
|
|
104
75
|
? parseSequencerAllowList(SEQ_ALLOWED_TEARDOWN_FN)
|
|
105
76
|
: getDefaultAllowedTeardownFunctions(),
|
|
77
|
+
...getTxSenderConfigFromEnv('SEQ'),
|
|
106
78
|
};
|
|
107
79
|
}
|
|
108
80
|
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ export * from './client/index.js';
|
|
|
2
2
|
export * from './config.js';
|
|
3
3
|
export * from './publisher/index.js';
|
|
4
4
|
export * from './sequencer/index.js';
|
|
5
|
+
export * from './tx_validator/aggregate_tx_validator.js';
|
|
6
|
+
export * from './tx_validator/data_validator.js';
|
|
5
7
|
|
|
6
8
|
// Used by the node to simulate public parts of transactions. Should these be moved to a shared library?
|
|
7
9
|
export * from './global_variable_builder/index.js';
|
package/src/publisher/config.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { type L1ContractAddresses } from '@aztec/ethereum';
|
|
1
|
+
import { type L1ContractAddresses, NULL_KEY } from '@aztec/ethereum';
|
|
2
|
+
|
|
3
|
+
import { type Hex } from 'viem';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* The configuration of the rollup transaction publisher.
|
|
@@ -37,5 +39,21 @@ export interface PublisherConfig {
|
|
|
37
39
|
/**
|
|
38
40
|
* The interval to wait between publish retries.
|
|
39
41
|
*/
|
|
40
|
-
|
|
42
|
+
l1PublishRetryIntervalMS: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getTxSenderConfigFromEnv(scope: 'PROVER' | 'SEQ'): Omit<TxSenderConfig, 'l1Contracts'> {
|
|
46
|
+
const { ETHEREUM_HOST, L1_CHAIN_ID } = process.env;
|
|
47
|
+
|
|
48
|
+
const PUBLISHER_PRIVATE_KEY = process.env[`${scope}_PUBLISHER_PRIVATE_KEY`];
|
|
49
|
+
const REQUIRED_CONFIRMATIONS = process.env[`${scope}_REQUIRED_CONFIRMATIONS`];
|
|
50
|
+
|
|
51
|
+
const publisherPrivateKey: Hex = PUBLISHER_PRIVATE_KEY ? `0x${PUBLISHER_PRIVATE_KEY.replace('0x', '')}` : NULL_KEY;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
rpcUrl: ETHEREUM_HOST ? ETHEREUM_HOST : '',
|
|
55
|
+
requiredConfirmations: REQUIRED_CONFIRMATIONS ? +REQUIRED_CONFIRMATIONS : 1,
|
|
56
|
+
publisherPrivateKey,
|
|
57
|
+
l1ChainId: L1_CHAIN_ID ? +L1_CHAIN_ID : 31337,
|
|
58
|
+
};
|
|
41
59
|
}
|
package/src/publisher/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { L1Publisher } from './l1-publisher.js';
|
|
|
3
3
|
import { ViemTxSender } from './viem-tx-sender.js';
|
|
4
4
|
|
|
5
5
|
export { L1Publisher } from './l1-publisher.js';
|
|
6
|
-
export { PublisherConfig } from './config.js';
|
|
6
|
+
export { PublisherConfig, TxSenderConfig, getTxSenderConfigFromEnv } from './config.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Returns a new instance of the L1Publisher.
|
|
@@ -46,8 +46,8 @@ export interface L1PublisherTxSender {
|
|
|
46
46
|
/** Returns the EOA used for sending txs to L1. */
|
|
47
47
|
getSenderAddress(): Promise<EthAddress>;
|
|
48
48
|
|
|
49
|
-
/** Returns the address
|
|
50
|
-
getSubmitterAddressForBlock(
|
|
49
|
+
/** Returns the address of the current proposer or zero if anyone can submit. */
|
|
50
|
+
getSubmitterAddressForBlock(): Promise<EthAddress>;
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Publishes tx effects to Availability Oracle.
|
|
@@ -134,11 +134,11 @@ export class L1Publisher implements L2BlockReceiver {
|
|
|
134
134
|
private log = createDebugLogger('aztec:sequencer:publisher');
|
|
135
135
|
|
|
136
136
|
constructor(private txSender: L1PublisherTxSender, config?: PublisherConfig) {
|
|
137
|
-
this.sleepTimeMs = config?.
|
|
137
|
+
this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
public async isItMyTurnToSubmit(
|
|
141
|
-
const submitter = await this.txSender.getSubmitterAddressForBlock(
|
|
140
|
+
public async isItMyTurnToSubmit(): Promise<boolean> {
|
|
141
|
+
const submitter = await this.txSender.getSubmitterAddressForBlock();
|
|
142
142
|
const sender = await this.txSender.getSenderAddress();
|
|
143
143
|
return submitter.isZero() || submitter.equals(sender);
|
|
144
144
|
}
|
|
@@ -77,12 +77,12 @@ export class ViemTxSender implements L1PublisherTxSender {
|
|
|
77
77
|
return Promise.resolve(EthAddress.fromString(this.account.address));
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
async getSubmitterAddressForBlock(
|
|
80
|
+
async getSubmitterAddressForBlock(): Promise<EthAddress> {
|
|
81
81
|
try {
|
|
82
|
-
const submitter = await this.rollupContract.read.
|
|
82
|
+
const submitter = await this.rollupContract.read.getCurrentProposer();
|
|
83
83
|
return EthAddress.fromString(submitter);
|
|
84
84
|
} catch (err) {
|
|
85
|
-
this.log.warn(`Failed to get submitter
|
|
85
|
+
this.log.warn(`Failed to get submitter: ${err}`);
|
|
86
86
|
return EthAddress.ZERO;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -42,6 +42,8 @@ export class Sequencer {
|
|
|
42
42
|
private pollingIntervalMs: number = 1000;
|
|
43
43
|
private maxTxsPerBlock = 32;
|
|
44
44
|
private minTxsPerBLock = 1;
|
|
45
|
+
private minSecondsBetweenBlocks = 0;
|
|
46
|
+
private maxSecondsBetweenBlocks = 0;
|
|
45
47
|
// TODO: zero values should not be allowed for the following 2 values in PROD
|
|
46
48
|
private _coinbase = EthAddress.ZERO;
|
|
47
49
|
private _feeRecipient = AztecAddress.ZERO;
|
|
@@ -50,6 +52,7 @@ export class Sequencer {
|
|
|
50
52
|
private allowedInSetup: AllowedElement[] = [];
|
|
51
53
|
private allowedInTeardown: AllowedElement[] = [];
|
|
52
54
|
private maxBlockSizeInBytes: number = 1024 * 1024;
|
|
55
|
+
private skipSubmitProofs: boolean = false;
|
|
53
56
|
|
|
54
57
|
public readonly tracer: Tracer;
|
|
55
58
|
|
|
@@ -77,15 +80,21 @@ export class Sequencer {
|
|
|
77
80
|
* @param config - New parameters.
|
|
78
81
|
*/
|
|
79
82
|
public updateConfig(config: SequencerConfig) {
|
|
80
|
-
if (config.transactionPollingIntervalMS) {
|
|
83
|
+
if (config.transactionPollingIntervalMS !== undefined) {
|
|
81
84
|
this.pollingIntervalMs = config.transactionPollingIntervalMS;
|
|
82
85
|
}
|
|
83
|
-
if (config.maxTxsPerBlock) {
|
|
86
|
+
if (config.maxTxsPerBlock !== undefined) {
|
|
84
87
|
this.maxTxsPerBlock = config.maxTxsPerBlock;
|
|
85
88
|
}
|
|
86
|
-
if (config.minTxsPerBlock) {
|
|
89
|
+
if (config.minTxsPerBlock !== undefined) {
|
|
87
90
|
this.minTxsPerBLock = config.minTxsPerBlock;
|
|
88
91
|
}
|
|
92
|
+
if (config.minSecondsBetweenBlocks !== undefined) {
|
|
93
|
+
this.minSecondsBetweenBlocks = config.minSecondsBetweenBlocks;
|
|
94
|
+
}
|
|
95
|
+
if (config.maxSecondsBetweenBlocks !== undefined) {
|
|
96
|
+
this.maxSecondsBetweenBlocks = config.maxSecondsBetweenBlocks;
|
|
97
|
+
}
|
|
89
98
|
if (config.coinbase) {
|
|
90
99
|
this._coinbase = config.coinbase;
|
|
91
100
|
}
|
|
@@ -95,13 +104,17 @@ export class Sequencer {
|
|
|
95
104
|
if (config.allowedInSetup) {
|
|
96
105
|
this.allowedInSetup = config.allowedInSetup;
|
|
97
106
|
}
|
|
98
|
-
if (config.maxBlockSizeInBytes) {
|
|
107
|
+
if (config.maxBlockSizeInBytes !== undefined) {
|
|
99
108
|
this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
|
|
100
109
|
}
|
|
101
110
|
// TODO(#5917) remove this. it is no longer needed since we don't need to whitelist functions in teardown
|
|
102
111
|
if (config.allowedInTeardown) {
|
|
103
112
|
this.allowedInTeardown = config.allowedInTeardown;
|
|
104
113
|
}
|
|
114
|
+
// TODO(palla/prover) This flag should not be needed: the sequencer should be initialized with a blockprover
|
|
115
|
+
// that does not return proofs at all (just simulates circuits), and use that to determine whether to submit
|
|
116
|
+
// proofs or not.
|
|
117
|
+
this.skipSubmitProofs = !!config.sequencerSkipSubmitProofs;
|
|
105
118
|
}
|
|
106
119
|
|
|
107
120
|
/**
|
|
@@ -174,17 +187,41 @@ export class Sequencer {
|
|
|
174
187
|
: Number(historicalHeader.globalVariables.blockNumber.toBigInt())) + 1;
|
|
175
188
|
|
|
176
189
|
// Do not go forward with new block if not my turn
|
|
177
|
-
if (!(await this.publisher.isItMyTurnToSubmit(
|
|
178
|
-
this.log.
|
|
190
|
+
if (!(await this.publisher.isItMyTurnToSubmit())) {
|
|
191
|
+
this.log.debug('Not my turn to submit block');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Compute time elapsed since the previous block
|
|
196
|
+
const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
|
|
197
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
198
|
+
const elapsedSinceLastBlock = currentTime - lastBlockTime;
|
|
199
|
+
|
|
200
|
+
// Do not go forward with new block if not enough time has passed since last block
|
|
201
|
+
if (this.minSecondsBetweenBlocks > 0 && elapsedSinceLastBlock < this.minSecondsBetweenBlocks) {
|
|
202
|
+
this.log.debug(
|
|
203
|
+
`Not creating block because not enough time has passed since last block (last block at ${lastBlockTime} current time ${currentTime})`,
|
|
204
|
+
);
|
|
179
205
|
return;
|
|
180
206
|
}
|
|
181
207
|
|
|
182
208
|
this.state = SequencerState.WAITING_FOR_TXS;
|
|
183
209
|
|
|
184
|
-
// Get txs to build the new block
|
|
185
|
-
const pendingTxs =
|
|
210
|
+
// Get txs to build the new block.
|
|
211
|
+
const pendingTxs = this.p2pClient.getTxs('pending');
|
|
212
|
+
|
|
213
|
+
// If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
|
|
186
214
|
if (pendingTxs.length < this.minTxsPerBLock) {
|
|
187
|
-
|
|
215
|
+
if (this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
|
|
216
|
+
this.log.debug(
|
|
217
|
+
`Creating block with only ${pendingTxs.length} txs as more than ${this.maxSecondsBetweenBlocks}s have passed since last block`,
|
|
218
|
+
);
|
|
219
|
+
} else {
|
|
220
|
+
this.log.debug(
|
|
221
|
+
`Not creating block because not enough txs in the pool (got ${pendingTxs.length} min ${this.minTxsPerBLock})`,
|
|
222
|
+
);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
188
225
|
}
|
|
189
226
|
this.log.debug(`Retrieved ${pendingTxs.length} txs from P2P pool`);
|
|
190
227
|
|
|
@@ -207,11 +244,15 @@ export class Sequencer {
|
|
|
207
244
|
// may break if we start emitting lots of log data from public-land.
|
|
208
245
|
const validTxs = this.takeTxsWithinMaxSize(allValidTxs);
|
|
209
246
|
|
|
210
|
-
if
|
|
247
|
+
// Bail if we don't have enough valid txs
|
|
248
|
+
if (!this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock) && validTxs.length < this.minTxsPerBLock) {
|
|
249
|
+
this.log.debug(
|
|
250
|
+
`Not creating block because not enough valid txs loaded from the pool (got ${validTxs.length} min ${this.minTxsPerBLock})`,
|
|
251
|
+
);
|
|
211
252
|
return;
|
|
212
253
|
}
|
|
213
254
|
|
|
214
|
-
await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader);
|
|
255
|
+
await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader, elapsedSinceLastBlock);
|
|
215
256
|
} catch (err) {
|
|
216
257
|
if (BlockProofError.isBlockProofError(err)) {
|
|
217
258
|
const txHashes = err.txHashes.filter(h => !h.isZero());
|
|
@@ -225,6 +266,11 @@ export class Sequencer {
|
|
|
225
266
|
}
|
|
226
267
|
}
|
|
227
268
|
|
|
269
|
+
/** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
|
|
270
|
+
private skipMinTxsPerBlockCheck(elapsed: number): boolean {
|
|
271
|
+
return this.maxSecondsBetweenBlocks > 0 && elapsed >= this.maxSecondsBetweenBlocks;
|
|
272
|
+
}
|
|
273
|
+
|
|
228
274
|
@trackSpan('Sequencer.buildBlockAndPublish', (_validTxs, newGlobalVariables, _historicalHeader) => ({
|
|
229
275
|
[Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber.toNumber(),
|
|
230
276
|
}))
|
|
@@ -232,6 +278,7 @@ export class Sequencer {
|
|
|
232
278
|
validTxs: Tx[],
|
|
233
279
|
newGlobalVariables: GlobalVariables,
|
|
234
280
|
historicalHeader: Header | undefined,
|
|
281
|
+
elapsedSinceLastBlock: number,
|
|
235
282
|
): Promise<void> {
|
|
236
283
|
const workTimer = new Timer();
|
|
237
284
|
this.state = SequencerState.CREATING_BLOCK;
|
|
@@ -242,7 +289,8 @@ export class Sequencer {
|
|
|
242
289
|
if (currentBlockNumber + 1 !== newGlobalVariables.blockNumber.toNumber()) {
|
|
243
290
|
throw new Error('New block was emitted while building block');
|
|
244
291
|
}
|
|
245
|
-
|
|
292
|
+
|
|
293
|
+
if (!(await this.publisher.isItMyTurnToSubmit())) {
|
|
246
294
|
throw new Error(`Not this sequencer turn to submit block`);
|
|
247
295
|
}
|
|
248
296
|
};
|
|
@@ -255,7 +303,7 @@ export class Sequencer {
|
|
|
255
303
|
);
|
|
256
304
|
|
|
257
305
|
// We create a fresh processor each time to reset any cached state (eg storage writes)
|
|
258
|
-
const processor =
|
|
306
|
+
const processor = this.publicProcessorFactory.create(historicalHeader, newGlobalVariables);
|
|
259
307
|
|
|
260
308
|
const numRealTxs = validTxs.length;
|
|
261
309
|
const blockSize = Math.max(2, numRealTxs);
|
|
@@ -272,7 +320,11 @@ export class Sequencer {
|
|
|
272
320
|
await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
|
|
273
321
|
}
|
|
274
322
|
|
|
275
|
-
|
|
323
|
+
// TODO: This check should be processedTxs.length < this.minTxsPerBLock, so we don't publish a block with
|
|
324
|
+
// less txs than the minimum. But that'd cause the entire block to be aborted and retried. Instead, we should
|
|
325
|
+
// go back to the p2p pool and load more txs until we hit our minTxsPerBLock target. Only if there are no txs
|
|
326
|
+
// we should bail.
|
|
327
|
+
if (processedTxs.length === 0 && !this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
|
|
276
328
|
this.log.verbose('No txs processed correctly to build block. Exiting');
|
|
277
329
|
this.prover.cancelBlock();
|
|
278
330
|
return;
|
|
@@ -309,10 +361,10 @@ export class Sequencer {
|
|
|
309
361
|
await this.publishL2Block(block);
|
|
310
362
|
this.log.info(`Submitted rollup block ${block.number} with ${processedTxs.length} transactions`);
|
|
311
363
|
|
|
312
|
-
// Submit the proof if we have configured this sequencer to run with
|
|
364
|
+
// Submit the proof if we have configured this sequencer to run with an actual prover.
|
|
313
365
|
// This is temporary while we submit one proof per block, but will have to change once we
|
|
314
366
|
// move onto proving batches of multiple blocks at a time.
|
|
315
|
-
if (aggregationObject && proof) {
|
|
367
|
+
if (aggregationObject && proof && !this.skipSubmitProofs) {
|
|
316
368
|
await this.publisher.submitProof(block.header, block.archive.root, aggregationObject, proof);
|
|
317
369
|
this.log.info(`Submitted proof for block ${block.number}`);
|
|
318
370
|
}
|
|
@@ -378,7 +430,15 @@ export class Sequencer {
|
|
|
378
430
|
this.l1ToL2MessageSource.getBlockNumber(),
|
|
379
431
|
]);
|
|
380
432
|
const min = Math.min(...syncedBlocks);
|
|
381
|
-
|
|
433
|
+
const [worldState, p2p, l2BlockSource, l1ToL2MessageSource] = syncedBlocks;
|
|
434
|
+
const result = min >= this.lastPublishedBlock;
|
|
435
|
+
this.log.debug(`Sync check to last published block ${this.lastPublishedBlock} ${result ? 'succeeded' : 'failed'}`, {
|
|
436
|
+
worldState,
|
|
437
|
+
p2p,
|
|
438
|
+
l2BlockSource,
|
|
439
|
+
l1ToL2MessageSource,
|
|
440
|
+
});
|
|
441
|
+
return result;
|
|
382
442
|
}
|
|
383
443
|
|
|
384
444
|
get coinbase(): EthAddress {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Tx, type TxValidator } from '@aztec/circuit-types';
|
|
2
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
3
|
+
|
|
4
|
+
export class DataTxValidator implements TxValidator<Tx> {
|
|
5
|
+
#log = createDebugLogger('aztec:sequencer:tx_validator:tx_data');
|
|
6
|
+
|
|
7
|
+
validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
|
|
8
|
+
const validTxs: Tx[] = [];
|
|
9
|
+
const invalidTxs: Tx[] = [];
|
|
10
|
+
for (const tx of txs) {
|
|
11
|
+
if (!this.#hasCorrectExecutionRequests(tx)) {
|
|
12
|
+
invalidTxs.push(tx);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
validTxs.push(tx);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return Promise.resolve([validTxs, invalidTxs]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#hasCorrectExecutionRequests(tx: Tx): boolean {
|
|
23
|
+
const callRequests = [
|
|
24
|
+
...tx.data.getRevertiblePublicCallRequests(),
|
|
25
|
+
...tx.data.getNonRevertiblePublicCallRequests(),
|
|
26
|
+
];
|
|
27
|
+
if (callRequests.length !== tx.enqueuedPublicFunctionCalls.length) {
|
|
28
|
+
this.#log.warn(
|
|
29
|
+
`Rejecting tx ${Tx.getHash(tx)} because of mismatch number of execution requests for public calls. Expected ${
|
|
30
|
+
callRequests.length
|
|
31
|
+
}. Got ${tx.enqueuedPublicFunctionCalls.length}.`,
|
|
32
|
+
);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const invalidExecutionRequestIndex = tx.enqueuedPublicFunctionCalls.findIndex(
|
|
37
|
+
(execRequest, i) => !execRequest.isForCallRequest(callRequests[i]),
|
|
38
|
+
);
|
|
39
|
+
if (invalidExecutionRequestIndex !== -1) {
|
|
40
|
+
this.#log.warn(
|
|
41
|
+
`Rejecting tx ${Tx.getHash(
|
|
42
|
+
tx,
|
|
43
|
+
)} because of incorrect execution requests for public call at index ${invalidExecutionRequestIndex}.`,
|
|
44
|
+
);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const teardownCallRequest = tx.data.getTeardownPublicCallRequest();
|
|
49
|
+
const isInvalidTeardownExecutionRequest =
|
|
50
|
+
(!teardownCallRequest && !tx.publicTeardownFunctionCall.isEmpty()) ||
|
|
51
|
+
(teardownCallRequest && !tx.publicTeardownFunctionCall.isForCallRequest(teardownCallRequest));
|
|
52
|
+
if (isInvalidTeardownExecutionRequest) {
|
|
53
|
+
this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} because of incorrect teardown execution requests.`);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// TODO: Check logs.
|
|
61
|
+
}
|
|
@@ -60,7 +60,9 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
60
60
|
fn =>
|
|
61
61
|
fn.contractAddress.equals(this.#gasTokenAddress) &&
|
|
62
62
|
fn.callContext.msgSender.equals(this.#gasTokenAddress) &&
|
|
63
|
-
fn.functionSelector.equals(
|
|
63
|
+
fn.callContext.functionSelector.equals(
|
|
64
|
+
GasTokenArtifact.functions.find(f => f.name === '_increase_public_balance')!,
|
|
65
|
+
) &&
|
|
64
66
|
fn.args[0].equals(feePayer) &&
|
|
65
67
|
!fn.callContext.isStaticCall &&
|
|
66
68
|
!fn.callContext.isDelegateCall,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type AllowedElement,
|
|
3
|
+
type PublicExecutionRequest,
|
|
4
|
+
PublicKernelType,
|
|
5
|
+
Tx,
|
|
6
|
+
type TxValidator,
|
|
7
|
+
} from '@aztec/circuit-types';
|
|
3
8
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
4
9
|
import { AbstractPhaseManager, ContractsDataSourcePublicDB } from '@aztec/simulator';
|
|
5
10
|
import { type ContractDataSource } from '@aztec/types/contracts';
|
|
@@ -47,7 +52,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
47
52
|
this.#log.warn(
|
|
48
53
|
`Rejecting tx ${Tx.getHash(tx)} because it calls setup function not on allow list: ${
|
|
49
54
|
setupFn.contractAddress
|
|
50
|
-
}:${setupFn.functionSelector}`,
|
|
55
|
+
}:${setupFn.callContext.functionSelector}`,
|
|
51
56
|
);
|
|
52
57
|
|
|
53
58
|
return false;
|
|
@@ -57,12 +62,15 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
57
62
|
return true;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
async isOnAllowList(publicCall:
|
|
65
|
+
async isOnAllowList(publicCall: PublicExecutionRequest, allowList: AllowedElement[]): Promise<boolean> {
|
|
61
66
|
if (publicCall.isEmpty()) {
|
|
62
67
|
return true;
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
const {
|
|
70
|
+
const {
|
|
71
|
+
contractAddress,
|
|
72
|
+
callContext: { functionSelector },
|
|
73
|
+
} = publicCall;
|
|
66
74
|
|
|
67
75
|
// do these checks first since they don't require the contract class
|
|
68
76
|
for (const entry of allowList) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Tx } from '@aztec/circuit-types';
|
|
2
2
|
import { type AztecAddress, type Fr, type FunctionSelector } from '@aztec/circuits.js';
|
|
3
|
+
import { computeVarArgsHash } from '@aztec/circuits.js/hash';
|
|
3
4
|
|
|
4
5
|
export function patchNonRevertibleFn(
|
|
5
6
|
tx: Tx,
|
|
@@ -25,13 +26,19 @@ function patchFn(
|
|
|
25
26
|
): { address: AztecAddress; selector: FunctionSelector } {
|
|
26
27
|
const fn = tx.enqueuedPublicFunctionCalls.at(-1 * index - 1)!;
|
|
27
28
|
fn.contractAddress = overrides.address ?? fn.contractAddress;
|
|
28
|
-
fn.functionSelector = overrides.selector;
|
|
29
|
+
fn.callContext.functionSelector = overrides.selector;
|
|
29
30
|
fn.args = overrides.args ?? fn.args;
|
|
30
31
|
fn.callContext.msgSender = overrides.msgSender ?? fn.callContext.msgSender;
|
|
31
|
-
tx.
|
|
32
|
+
tx.enqueuedPublicFunctionCalls[index] = fn;
|
|
33
|
+
|
|
34
|
+
const request = tx.data.forPublic![where].publicCallStack[index];
|
|
35
|
+
request.item.contractAddress = fn.contractAddress;
|
|
36
|
+
request.item.callContext = fn.callContext;
|
|
37
|
+
request.item.argsHash = computeVarArgsHash(fn.args);
|
|
38
|
+
tx.data.forPublic![where].publicCallStack[index] = request;
|
|
32
39
|
|
|
33
40
|
return {
|
|
34
41
|
address: fn.contractAddress,
|
|
35
|
-
selector: fn.functionSelector,
|
|
42
|
+
selector: fn.callContext.functionSelector,
|
|
36
43
|
};
|
|
37
44
|
}
|
|
@@ -6,6 +6,7 @@ import { type ContractDataSource } from '@aztec/types/contracts';
|
|
|
6
6
|
import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
7
7
|
|
|
8
8
|
import { AggregateTxValidator } from './aggregate_tx_validator.js';
|
|
9
|
+
import { DataTxValidator } from './data_validator.js';
|
|
9
10
|
import { DoubleSpendTxValidator } from './double_spend_validator.js';
|
|
10
11
|
import { GasTxValidator } from './gas_validator.js';
|
|
11
12
|
import { MetadataTxValidator } from './metadata_validator.js';
|
|
@@ -20,6 +21,7 @@ export class TxValidatorFactory {
|
|
|
20
21
|
|
|
21
22
|
validatorForNewTxs(globalVariables: GlobalVariables, setupAllowList: AllowedElement[]): TxValidator<Tx> {
|
|
22
23
|
return new AggregateTxValidator(
|
|
24
|
+
new DataTxValidator(),
|
|
23
25
|
new MetadataTxValidator(globalVariables),
|
|
24
26
|
new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb)),
|
|
25
27
|
new PhasesTxValidator(this.contractDataSource, setupAllowList),
|