@aztec/validator-client 0.0.1-commit.7d4e6cd → 0.0.1-commit.7ffbba4
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/README.md +95 -24
- package/dest/block_proposal_handler.d.ts +10 -10
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +76 -76
- package/dest/checkpoint_builder.d.ts +31 -25
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +114 -41
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +33 -14
- package/dest/duties/validation_service.d.ts +20 -7
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +69 -22
- package/dest/factory.d.ts +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +3 -2
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/key_store/ha_key_store.d.ts +99 -0
- package/dest/key_store/ha_key_store.d.ts.map +1 -0
- package/dest/key_store/ha_key_store.js +208 -0
- package/dest/key_store/index.d.ts +2 -1
- package/dest/key_store/index.d.ts.map +1 -1
- package/dest/key_store/index.js +1 -0
- package/dest/key_store/interface.d.ts +36 -6
- package/dest/key_store/interface.d.ts.map +1 -1
- package/dest/key_store/local_key_store.d.ts +10 -5
- package/dest/key_store/local_key_store.d.ts.map +1 -1
- package/dest/key_store/local_key_store.js +8 -4
- package/dest/key_store/node_keystore_adapter.d.ts +18 -5
- package/dest/key_store/node_keystore_adapter.d.ts.map +1 -1
- package/dest/key_store/node_keystore_adapter.js +18 -4
- package/dest/key_store/web3signer_key_store.d.ts +10 -5
- package/dest/key_store/web3signer_key_store.d.ts.map +1 -1
- package/dest/key_store/web3signer_key_store.js +8 -4
- package/dest/metrics.d.ts +12 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +46 -5
- package/dest/validator.d.ts +45 -18
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +262 -98
- package/package.json +21 -17
- package/src/block_proposal_handler.ts +93 -95
- package/src/checkpoint_builder.ts +171 -48
- package/src/config.ts +32 -13
- package/src/duties/validation_service.ts +94 -25
- package/src/factory.ts +2 -0
- package/src/index.ts +0 -1
- package/src/key_store/ha_key_store.ts +269 -0
- package/src/key_store/index.ts +1 -0
- package/src/key_store/interface.ts +44 -5
- package/src/key_store/local_key_store.ts +13 -4
- package/src/key_store/node_keystore_adapter.ts +27 -4
- package/src/key_store/web3signer_key_store.ts +17 -4
- package/src/metrics.ts +63 -6
- package/src/validator.ts +326 -116
- package/dest/tx_validator/index.d.ts +0 -3
- package/dest/tx_validator/index.d.ts.map +0 -1
- package/dest/tx_validator/index.js +0 -2
- package/dest/tx_validator/nullifier_cache.d.ts +0 -14
- package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
- package/dest/tx_validator/nullifier_cache.js +0 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.js +0 -53
- package/src/tx_validator/index.ts +0 -2
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -133
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/validator-client",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.7ffbba4",
|
|
4
4
|
"main": "dest/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -64,31 +64,35 @@
|
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@aztec/blob-client": "0.0.1-commit.
|
|
68
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
69
|
-
"@aztec/constants": "0.0.1-commit.
|
|
70
|
-
"@aztec/epoch-cache": "0.0.1-commit.
|
|
71
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
72
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
73
|
-
"@aztec/node-keystore": "0.0.1-commit.
|
|
74
|
-
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.
|
|
75
|
-
"@aztec/p2p": "0.0.1-commit.
|
|
76
|
-
"@aztec/protocol-contracts": "0.0.1-commit.
|
|
77
|
-
"@aztec/prover-client": "0.0.1-commit.
|
|
78
|
-
"@aztec/simulator": "0.0.1-commit.
|
|
79
|
-
"@aztec/slasher": "0.0.1-commit.
|
|
80
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
81
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
67
|
+
"@aztec/blob-client": "0.0.1-commit.7ffbba4",
|
|
68
|
+
"@aztec/blob-lib": "0.0.1-commit.7ffbba4",
|
|
69
|
+
"@aztec/constants": "0.0.1-commit.7ffbba4",
|
|
70
|
+
"@aztec/epoch-cache": "0.0.1-commit.7ffbba4",
|
|
71
|
+
"@aztec/ethereum": "0.0.1-commit.7ffbba4",
|
|
72
|
+
"@aztec/foundation": "0.0.1-commit.7ffbba4",
|
|
73
|
+
"@aztec/node-keystore": "0.0.1-commit.7ffbba4",
|
|
74
|
+
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.7ffbba4",
|
|
75
|
+
"@aztec/p2p": "0.0.1-commit.7ffbba4",
|
|
76
|
+
"@aztec/protocol-contracts": "0.0.1-commit.7ffbba4",
|
|
77
|
+
"@aztec/prover-client": "0.0.1-commit.7ffbba4",
|
|
78
|
+
"@aztec/simulator": "0.0.1-commit.7ffbba4",
|
|
79
|
+
"@aztec/slasher": "0.0.1-commit.7ffbba4",
|
|
80
|
+
"@aztec/stdlib": "0.0.1-commit.7ffbba4",
|
|
81
|
+
"@aztec/telemetry-client": "0.0.1-commit.7ffbba4",
|
|
82
|
+
"@aztec/validator-ha-signer": "0.0.1-commit.7ffbba4",
|
|
82
83
|
"koa": "^2.16.1",
|
|
83
84
|
"koa-router": "^13.1.1",
|
|
84
85
|
"tslib": "^2.4.0",
|
|
85
86
|
"viem": "npm:@aztec/viem@2.38.2"
|
|
86
87
|
},
|
|
87
88
|
"devDependencies": {
|
|
89
|
+
"@aztec/archiver": "0.0.1-commit.7ffbba4",
|
|
90
|
+
"@aztec/world-state": "0.0.1-commit.7ffbba4",
|
|
91
|
+
"@electric-sql/pglite": "^0.3.14",
|
|
88
92
|
"@jest/globals": "^30.0.0",
|
|
89
93
|
"@types/jest": "^30.0.0",
|
|
90
94
|
"@types/node": "^22.15.17",
|
|
91
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
95
|
+
"@typescript/native-preview": "7.0.0-dev.20260113.1",
|
|
92
96
|
"jest": "^30.0.0",
|
|
93
97
|
"jest-mock-extended": "^4.0.0",
|
|
94
98
|
"ts-node": "^10.9.1",
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
+
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
3
|
import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
4
|
+
import { pick } from '@aztec/foundation/collection';
|
|
3
5
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
6
|
import { TimeoutError } from '@aztec/foundation/error';
|
|
5
7
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
8
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
7
9
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
8
10
|
import type { P2P, PeerId } from '@aztec/p2p';
|
|
9
|
-
import { TxProvider } from '@aztec/p2p';
|
|
10
11
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
11
|
-
import type {
|
|
12
|
-
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
13
|
-
import
|
|
12
|
+
import type { BlockData, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
13
|
+
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
14
|
+
import { Gas } from '@aztec/stdlib/gas';
|
|
15
|
+
import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
14
16
|
import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
15
17
|
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
16
|
-
import {
|
|
18
|
+
import type { CheckpointGlobalVariables, FailedTx, Tx } from '@aztec/stdlib/tx';
|
|
17
19
|
import {
|
|
18
20
|
ReExFailedTxsError,
|
|
19
21
|
ReExStateMismatchError,
|
|
@@ -39,7 +41,7 @@ export type BlockProposalValidationFailureReason =
|
|
|
39
41
|
| 'unknown_error';
|
|
40
42
|
|
|
41
43
|
type ReexecuteTransactionsResult = {
|
|
42
|
-
block:
|
|
44
|
+
block: L2Block;
|
|
43
45
|
failedTxs: FailedTx[];
|
|
44
46
|
reexecutionTimeMs: number;
|
|
45
47
|
totalManaUsed: number;
|
|
@@ -72,8 +74,9 @@ export class BlockProposalHandler {
|
|
|
72
74
|
private worldState: WorldStateSynchronizer,
|
|
73
75
|
private blockSource: L2BlockSource & L2BlockSink,
|
|
74
76
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
75
|
-
private txProvider:
|
|
77
|
+
private txProvider: ITxProvider,
|
|
76
78
|
private blockProposalValidator: BlockProposalValidator,
|
|
79
|
+
private epochCache: EpochCache,
|
|
77
80
|
private config: ValidatorClientFullConfig,
|
|
78
81
|
private metrics?: ValidatorMetrics,
|
|
79
82
|
private dateProvider: DateProvider = new DateProvider(),
|
|
@@ -86,25 +89,28 @@ export class BlockProposalHandler {
|
|
|
86
89
|
this.tracer = telemetry.getTracer('BlockProposalHandler');
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
// Non-validator handler that re-executes for monitoring but does not attest.
|
|
92
|
+
register(p2pClient: P2P, shouldReexecute: boolean): BlockProposalHandler {
|
|
93
|
+
// Non-validator handler that processes or re-executes for monitoring but does not attest.
|
|
91
94
|
// Returns boolean indicating whether the proposal was valid.
|
|
92
95
|
const handler = async (proposal: BlockProposal, proposalSender: PeerId): Promise<boolean> => {
|
|
93
96
|
try {
|
|
94
|
-
const
|
|
97
|
+
const { slotNumber, blockNumber } = proposal;
|
|
98
|
+
const result = await this.handleBlockProposal(proposal, proposalSender, shouldReexecute);
|
|
95
99
|
if (result.isValid) {
|
|
96
|
-
this.log.info(`Non-validator
|
|
100
|
+
this.log.info(`Non-validator block proposal ${blockNumber} at slot ${slotNumber} handled`, {
|
|
97
101
|
blockNumber: result.blockNumber,
|
|
102
|
+
slotNumber,
|
|
98
103
|
reexecutionTimeMs: result.reexecutionResult?.reexecutionTimeMs,
|
|
99
104
|
totalManaUsed: result.reexecutionResult?.totalManaUsed,
|
|
100
105
|
numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0,
|
|
106
|
+
reexecuted: shouldReexecute,
|
|
101
107
|
});
|
|
102
108
|
return true;
|
|
103
109
|
} else {
|
|
104
|
-
this.log.warn(
|
|
105
|
-
blockNumber
|
|
106
|
-
reason: result.reason,
|
|
107
|
-
|
|
110
|
+
this.log.warn(
|
|
111
|
+
`Non-validator block proposal ${blockNumber} at slot ${slotNumber} failed processing with ${result.reason}`,
|
|
112
|
+
{ blockNumber: result.blockNumber, slotNumber, reason: result.reason },
|
|
113
|
+
);
|
|
108
114
|
return false;
|
|
109
115
|
}
|
|
110
116
|
} catch (error) {
|
|
@@ -140,23 +146,23 @@ export class BlockProposalHandler {
|
|
|
140
146
|
|
|
141
147
|
// Check that the proposal is from the current proposer, or the next proposer
|
|
142
148
|
// This should have been handled by the p2p layer, but we double check here out of caution
|
|
143
|
-
const
|
|
144
|
-
if (
|
|
149
|
+
const validationResult = await this.blockProposalValidator.validate(proposal);
|
|
150
|
+
if (validationResult.result !== 'accept') {
|
|
145
151
|
this.log.warn(`Proposal is not valid, skipping processing`, proposalInfo);
|
|
146
152
|
return { isValid: false, reason: 'invalid_proposal' };
|
|
147
153
|
}
|
|
148
154
|
|
|
149
155
|
// Check that the parent proposal is a block we know, otherwise reexecution would fail
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
156
|
+
const parentBlock = await this.getParentBlock(proposal);
|
|
157
|
+
if (parentBlock === undefined) {
|
|
152
158
|
this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
|
|
153
159
|
return { isValid: false, reason: 'parent_block_not_found' };
|
|
154
160
|
}
|
|
155
161
|
|
|
156
|
-
// Check that the parent block's slot is
|
|
157
|
-
if (
|
|
158
|
-
this.log.warn(`Parent block slot is greater than
|
|
159
|
-
parentBlockSlot:
|
|
162
|
+
// Check that the parent block's slot is not greater than the proposal's slot.
|
|
163
|
+
if (parentBlock !== 'genesis' && parentBlock.header.getSlot() > slotNumber) {
|
|
164
|
+
this.log.warn(`Parent block slot is greater than proposal slot, skipping processing`, {
|
|
165
|
+
parentBlockSlot: parentBlock.header.getSlot().toString(),
|
|
160
166
|
proposalSlot: slotNumber.toString(),
|
|
161
167
|
...proposalInfo,
|
|
162
168
|
});
|
|
@@ -165,9 +171,9 @@ export class BlockProposalHandler {
|
|
|
165
171
|
|
|
166
172
|
// Compute the block number based on the parent block
|
|
167
173
|
const blockNumber =
|
|
168
|
-
|
|
174
|
+
parentBlock === 'genesis'
|
|
169
175
|
? BlockNumber(INITIAL_L2_BLOCK_NUM)
|
|
170
|
-
: BlockNumber(
|
|
176
|
+
: BlockNumber(parentBlock.header.getBlockNumber() + 1);
|
|
171
177
|
|
|
172
178
|
// Check that this block number does not exist already
|
|
173
179
|
const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
|
|
@@ -183,8 +189,17 @@ export class BlockProposalHandler {
|
|
|
183
189
|
deadline: this.getReexecutionDeadline(slotNumber, config),
|
|
184
190
|
});
|
|
185
191
|
|
|
192
|
+
// If reexecution is disabled, bail. We are just interested in triggering tx collection.
|
|
193
|
+
if (!shouldReexecute) {
|
|
194
|
+
this.log.info(
|
|
195
|
+
`Received valid block ${blockNumber} proposal at index ${proposal.indexWithinCheckpoint} on slot ${slotNumber}`,
|
|
196
|
+
proposalInfo,
|
|
197
|
+
);
|
|
198
|
+
return { isValid: true, blockNumber };
|
|
199
|
+
}
|
|
200
|
+
|
|
186
201
|
// Compute the checkpoint number for this block and validate checkpoint consistency
|
|
187
|
-
const checkpointResult =
|
|
202
|
+
const checkpointResult = this.computeCheckpointNumber(proposal, parentBlock, proposalInfo);
|
|
188
203
|
if (checkpointResult.reason) {
|
|
189
204
|
return { isValid: false, blockNumber, reason: checkpointResult.reason };
|
|
190
205
|
}
|
|
@@ -209,40 +224,44 @@ export class BlockProposalHandler {
|
|
|
209
224
|
return { isValid: false, blockNumber, reason: 'txs_not_available' };
|
|
210
225
|
}
|
|
211
226
|
|
|
227
|
+
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
228
|
+
const epoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
|
|
229
|
+
const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
|
|
230
|
+
.filter(c => c.checkpointNumber < checkpointNumber)
|
|
231
|
+
.map(c => c.checkpointOutHash);
|
|
232
|
+
|
|
212
233
|
// Try re-executing the transactions in the proposal if needed
|
|
213
234
|
let reexecutionResult;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
235
|
+
try {
|
|
236
|
+
this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
|
|
237
|
+
reexecutionResult = await this.reexecuteTransactions(
|
|
238
|
+
proposal,
|
|
239
|
+
blockNumber,
|
|
240
|
+
checkpointNumber,
|
|
241
|
+
txs,
|
|
242
|
+
l1ToL2Messages,
|
|
243
|
+
previousCheckpointOutHashes,
|
|
244
|
+
);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
|
|
247
|
+
const reason = this.getReexecuteFailureReason(error);
|
|
248
|
+
return { isValid: false, blockNumber, reason, reexecutionResult };
|
|
229
249
|
}
|
|
230
250
|
|
|
231
251
|
// If we succeeded, push this block into the archiver (unless disabled)
|
|
232
|
-
// TODO(palla/mbps): Change default to false once block sync is stable.
|
|
233
252
|
if (reexecutionResult?.block && this.config.skipPushProposedBlocksToArchiver === false) {
|
|
234
253
|
await this.blockSource.addBlock(reexecutionResult?.block);
|
|
235
254
|
}
|
|
236
255
|
|
|
237
256
|
this.log.info(
|
|
238
|
-
`Successfully
|
|
239
|
-
proposalInfo,
|
|
257
|
+
`Successfully re-executed block ${blockNumber} proposal at index ${proposal.indexWithinCheckpoint} on slot ${slotNumber}`,
|
|
258
|
+
{ ...proposalInfo, ...pick(reexecutionResult, 'reexecutionTimeMs', 'totalManaUsed') },
|
|
240
259
|
);
|
|
241
260
|
|
|
242
261
|
return { isValid: true, blockNumber, reexecutionResult };
|
|
243
262
|
}
|
|
244
263
|
|
|
245
|
-
private async getParentBlock(proposal: BlockProposal): Promise<'genesis' |
|
|
264
|
+
private async getParentBlock(proposal: BlockProposal): Promise<'genesis' | BlockData | undefined> {
|
|
246
265
|
const parentArchive = proposal.blockHeader.lastArchive.root;
|
|
247
266
|
const slot = proposal.slotNumber;
|
|
248
267
|
const config = this.checkpointsBuilder.getConfig();
|
|
@@ -258,12 +277,11 @@ export class BlockProposalHandler {
|
|
|
258
277
|
|
|
259
278
|
try {
|
|
260
279
|
return (
|
|
261
|
-
(await this.blockSource.
|
|
280
|
+
(await this.blockSource.getBlockDataByArchive(parentArchive)) ??
|
|
262
281
|
(timeoutDurationMs <= 0
|
|
263
282
|
? undefined
|
|
264
283
|
: await retryUntil(
|
|
265
|
-
() =>
|
|
266
|
-
this.blockSource.syncImmediate().then(() => this.blockSource.getBlockHeaderByArchive(parentArchive)),
|
|
284
|
+
() => this.blockSource.syncImmediate().then(() => this.blockSource.getBlockDataByArchive(parentArchive)),
|
|
267
285
|
'force archiver sync',
|
|
268
286
|
timeoutDurationMs / 1000,
|
|
269
287
|
0.5,
|
|
@@ -279,12 +297,12 @@ export class BlockProposalHandler {
|
|
|
279
297
|
}
|
|
280
298
|
}
|
|
281
299
|
|
|
282
|
-
private
|
|
300
|
+
private computeCheckpointNumber(
|
|
283
301
|
proposal: BlockProposal,
|
|
284
|
-
|
|
302
|
+
parentBlock: 'genesis' | BlockData,
|
|
285
303
|
proposalInfo: object,
|
|
286
|
-
):
|
|
287
|
-
if (
|
|
304
|
+
): CheckpointComputationResult {
|
|
305
|
+
if (parentBlock === 'genesis') {
|
|
288
306
|
// First block is in checkpoint 1
|
|
289
307
|
if (proposal.indexWithinCheckpoint !== 0) {
|
|
290
308
|
this.log.warn(`First block proposal has non-zero indexWithinCheckpoint`, proposalInfo);
|
|
@@ -293,19 +311,9 @@ export class BlockProposalHandler {
|
|
|
293
311
|
return { checkpointNumber: CheckpointNumber.INITIAL };
|
|
294
312
|
}
|
|
295
313
|
|
|
296
|
-
// Get the parent block to find its checkpoint number
|
|
297
|
-
// TODO(palla/mbps): The block header should include the checkpoint number to avoid this lookup,
|
|
298
|
-
// or at least the L2BlockSource should return a different struct that includes it.
|
|
299
|
-
const parentBlockNumber = parentBlockHeader.getBlockNumber();
|
|
300
|
-
const parentBlock = await this.blockSource.getL2BlockNew(parentBlockNumber);
|
|
301
|
-
if (!parentBlock) {
|
|
302
|
-
this.log.warn(`Parent block ${parentBlockNumber} not found in archiver`, proposalInfo);
|
|
303
|
-
return { reason: 'invalid_proposal' };
|
|
304
|
-
}
|
|
305
|
-
|
|
306
314
|
if (proposal.indexWithinCheckpoint === 0) {
|
|
307
315
|
// If this is the first block in a new checkpoint, increment the checkpoint number
|
|
308
|
-
if (!(proposal.blockHeader.getSlot() >
|
|
316
|
+
if (!(proposal.blockHeader.getSlot() > parentBlock.header.getSlot())) {
|
|
309
317
|
this.log.warn(`Slot should be greater than parent block slot for first block in checkpoint`, proposalInfo);
|
|
310
318
|
return { reason: 'invalid_proposal' };
|
|
311
319
|
}
|
|
@@ -317,7 +325,7 @@ export class BlockProposalHandler {
|
|
|
317
325
|
this.log.warn(`Non-sequential indexWithinCheckpoint`, proposalInfo);
|
|
318
326
|
return { reason: 'invalid_proposal' };
|
|
319
327
|
}
|
|
320
|
-
if (proposal.blockHeader.getSlot() !==
|
|
328
|
+
if (proposal.blockHeader.getSlot() !== parentBlock.header.getSlot()) {
|
|
321
329
|
this.log.warn(`Slot should be equal to parent block slot for non-first block in checkpoint`, proposalInfo);
|
|
322
330
|
return { reason: 'invalid_proposal' };
|
|
323
331
|
}
|
|
@@ -338,7 +346,7 @@ export class BlockProposalHandler {
|
|
|
338
346
|
*/
|
|
339
347
|
private validateNonFirstBlockInCheckpoint(
|
|
340
348
|
proposal: BlockProposal,
|
|
341
|
-
parentBlock:
|
|
349
|
+
parentBlock: BlockData,
|
|
342
350
|
proposalInfo: object,
|
|
343
351
|
): CheckpointComputationResult | undefined {
|
|
344
352
|
const proposalGlobals = proposal.blockHeader.globalVariables;
|
|
@@ -414,31 +422,7 @@ export class BlockProposalHandler {
|
|
|
414
422
|
|
|
415
423
|
private getReexecutionDeadline(slot: SlotNumber, config: { l1GenesisTime: bigint; slotDuration: number }): Date {
|
|
416
424
|
const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
|
|
417
|
-
|
|
418
|
-
return new Date(nextSlotTimestampSeconds * 1000 - msNeededForPropagationAndPublishing);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Gets all prior blocks in the same checkpoint (same slot and checkpoint number) up to but not including upToBlockNumber.
|
|
423
|
-
*/
|
|
424
|
-
private async getBlocksInCheckpoint(
|
|
425
|
-
slot: SlotNumber,
|
|
426
|
-
upToBlockNumber: BlockNumber,
|
|
427
|
-
checkpointNumber: CheckpointNumber,
|
|
428
|
-
): Promise<L2BlockNew[]> {
|
|
429
|
-
const blocks: L2BlockNew[] = [];
|
|
430
|
-
let currentBlockNumber = BlockNumber(upToBlockNumber - 1);
|
|
431
|
-
|
|
432
|
-
while (currentBlockNumber >= INITIAL_L2_BLOCK_NUM) {
|
|
433
|
-
const block = await this.blockSource.getL2BlockNew(currentBlockNumber);
|
|
434
|
-
if (!block || block.header.getSlot() !== slot || block.checkpointNumber !== checkpointNumber) {
|
|
435
|
-
break;
|
|
436
|
-
}
|
|
437
|
-
blocks.unshift(block);
|
|
438
|
-
currentBlockNumber = BlockNumber(currentBlockNumber - 1);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return blocks;
|
|
425
|
+
return new Date(nextSlotTimestampSeconds * 1000);
|
|
442
426
|
}
|
|
443
427
|
|
|
444
428
|
private getReexecuteFailureReason(err: any) {
|
|
@@ -459,6 +443,7 @@ export class BlockProposalHandler {
|
|
|
459
443
|
checkpointNumber: CheckpointNumber,
|
|
460
444
|
txs: Tx[],
|
|
461
445
|
l1ToL2Messages: Fr[],
|
|
446
|
+
previousCheckpointOutHashes: Fr[],
|
|
462
447
|
): Promise<ReexecuteTransactionsResult> {
|
|
463
448
|
const { blockHeader, txHashes } = proposal;
|
|
464
449
|
|
|
@@ -473,18 +458,21 @@ export class BlockProposalHandler {
|
|
|
473
458
|
const slot = proposal.slotNumber;
|
|
474
459
|
const config = this.checkpointsBuilder.getConfig();
|
|
475
460
|
|
|
476
|
-
// Get prior blocks in this checkpoint (same slot
|
|
477
|
-
const
|
|
461
|
+
// Get prior blocks in this checkpoint (same slot before current block)
|
|
462
|
+
const allBlocksInSlot = await this.blockSource.getBlocksForSlot(slot);
|
|
463
|
+
const priorBlocks = allBlocksInSlot.filter(b => b.number < blockNumber && b.header.getSlot() === slot);
|
|
478
464
|
|
|
479
465
|
// Fork before the block to be built
|
|
480
466
|
const parentBlockNumber = BlockNumber(blockNumber - 1);
|
|
481
|
-
|
|
467
|
+
await this.worldState.syncImmediate(parentBlockNumber);
|
|
468
|
+
await using fork = await this.worldState.fork(parentBlockNumber);
|
|
482
469
|
|
|
483
|
-
// Build checkpoint constants from proposal (excludes blockNumber
|
|
470
|
+
// Build checkpoint constants from proposal (excludes blockNumber which is per-block)
|
|
484
471
|
const constants: CheckpointGlobalVariables = {
|
|
485
472
|
chainId: new Fr(config.l1ChainId),
|
|
486
473
|
version: new Fr(config.rollupVersion),
|
|
487
474
|
slotNumber: slot,
|
|
475
|
+
timestamp: blockHeader.globalVariables.timestamp,
|
|
488
476
|
coinbase: blockHeader.globalVariables.coinbase,
|
|
489
477
|
feeRecipient: blockHeader.globalVariables.feeRecipient,
|
|
490
478
|
gasFees: blockHeader.globalVariables.gasFees,
|
|
@@ -494,25 +482,35 @@ export class BlockProposalHandler {
|
|
|
494
482
|
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
495
483
|
checkpointNumber,
|
|
496
484
|
constants,
|
|
485
|
+
0n, // only takes effect in the following checkpoint.
|
|
497
486
|
l1ToL2Messages,
|
|
487
|
+
previousCheckpointOutHashes,
|
|
498
488
|
fork,
|
|
499
489
|
priorBlocks,
|
|
490
|
+
this.log.getBindings(),
|
|
500
491
|
);
|
|
501
492
|
|
|
502
493
|
// Build the new block
|
|
503
494
|
const deadline = this.getReexecutionDeadline(slot, config);
|
|
495
|
+
const maxBlockGas =
|
|
496
|
+
this.config.validateMaxL2BlockGas !== undefined || this.config.validateMaxDABlockGas !== undefined
|
|
497
|
+
? new Gas(this.config.validateMaxDABlockGas ?? Infinity, this.config.validateMaxL2BlockGas ?? Infinity)
|
|
498
|
+
: undefined;
|
|
504
499
|
const result = await checkpointBuilder.buildBlock(txs, blockNumber, blockHeader.globalVariables.timestamp, {
|
|
505
500
|
deadline,
|
|
506
501
|
expectedEndState: blockHeader.state,
|
|
502
|
+
maxTransactions: this.config.validateMaxTxsPerBlock,
|
|
503
|
+
maxBlockGas,
|
|
507
504
|
});
|
|
508
505
|
|
|
509
506
|
const { block, failedTxs } = result;
|
|
510
507
|
const numFailedTxs = failedTxs.length;
|
|
511
508
|
|
|
512
|
-
this.log.verbose(`
|
|
509
|
+
this.log.verbose(`Block proposal ${blockNumber} at slot ${slot} transaction re-execution complete`, {
|
|
513
510
|
numFailedTxs,
|
|
514
511
|
numProposalTxs: txHashes.length,
|
|
515
512
|
numProcessedTxs: block.body.txEffects.length,
|
|
513
|
+
blockNumber,
|
|
516
514
|
slot,
|
|
517
515
|
});
|
|
518
516
|
|