@aztec/slasher 3.0.3 → 3.9.9-nightly.20260312
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 +24 -14
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +29 -17
- package/dest/empire_slasher_client.d.ts +1 -1
- package/dest/empire_slasher_client.d.ts.map +1 -1
- package/dest/empire_slasher_client.js +0 -8
- package/dest/factory/create_facade.d.ts +2 -2
- package/dest/factory/create_facade.d.ts.map +1 -1
- package/dest/factory/create_facade.js +26 -3
- package/dest/factory/create_implementation.d.ts +3 -2
- package/dest/factory/create_implementation.d.ts.map +1 -1
- package/dest/factory/create_implementation.js +11 -7
- package/dest/factory/get_settings.d.ts +2 -2
- package/dest/factory/get_settings.d.ts.map +1 -1
- package/dest/generated/slasher-defaults.d.ts +21 -0
- package/dest/generated/slasher-defaults.d.ts.map +1 -0
- package/dest/generated/slasher-defaults.js +21 -0
- package/dest/slash_offenses_collector.d.ts +5 -2
- package/dest/slash_offenses_collector.d.ts.map +1 -1
- package/dest/slash_offenses_collector.js +2 -2
- package/dest/slasher_client_facade.d.ts +3 -2
- package/dest/slasher_client_facade.d.ts.map +1 -1
- package/dest/slasher_client_facade.js +4 -2
- package/dest/stores/offenses_store.d.ts +1 -1
- package/dest/stores/offenses_store.d.ts.map +1 -1
- package/dest/stores/offenses_store.js +4 -2
- package/dest/stores/payloads_store.d.ts +1 -1
- package/dest/stores/payloads_store.d.ts.map +1 -1
- package/dest/stores/payloads_store.js +6 -3
- package/dest/tally_slasher_client.d.ts +2 -2
- package/dest/tally_slasher_client.d.ts.map +1 -1
- package/dest/tally_slasher_client.js +10 -5
- package/dest/watchers/attestations_block_watcher.d.ts +7 -6
- package/dest/watchers/attestations_block_watcher.d.ts.map +1 -1
- package/dest/watchers/attestations_block_watcher.js +40 -34
- package/dest/watchers/epoch_prune_watcher.d.ts +9 -7
- package/dest/watchers/epoch_prune_watcher.d.ts.map +1 -1
- package/dest/watchers/epoch_prune_watcher.js +56 -17
- package/package.json +14 -12
- package/src/config.ts +32 -17
- package/src/empire_slasher_client.ts +0 -8
- package/src/factory/create_facade.ts +32 -3
- package/src/factory/create_implementation.ts +28 -3
- package/src/factory/get_settings.ts +2 -2
- package/src/generated/slasher-defaults.ts +23 -0
- package/src/slash_offenses_collector.ts +8 -3
- package/src/slasher_client_facade.ts +2 -0
- package/src/stores/offenses_store.ts +4 -2
- package/src/stores/payloads_store.ts +7 -4
- package/src/tally_slasher_client.ts +12 -6
- package/src/watchers/attestations_block_watcher.ts +57 -44
- package/src/watchers/epoch_prune_watcher.ts +84 -25
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
-
import { BlockNumber,
|
|
3
|
-
import { merge, pick } from '@aztec/foundation/collection';
|
|
2
|
+
import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { chunkBy, merge, pick } from '@aztec/foundation/collection';
|
|
4
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
5
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
5
6
|
import {
|
|
6
7
|
EthAddress,
|
|
7
8
|
L2Block,
|
|
8
|
-
type L2BlockPruneEvent,
|
|
9
9
|
type L2BlockSourceEventEmitter,
|
|
10
10
|
L2BlockSourceEvents,
|
|
11
|
+
type L2PruneUnprovenEvent,
|
|
11
12
|
} from '@aztec/stdlib/block';
|
|
12
13
|
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
13
14
|
import type {
|
|
14
|
-
|
|
15
|
+
ICheckpointBlockBuilder,
|
|
16
|
+
ICheckpointsBuilder,
|
|
15
17
|
ITxProvider,
|
|
16
18
|
MerkleTreeWriteOperations,
|
|
17
19
|
SlasherConfig,
|
|
18
20
|
} from '@aztec/stdlib/interfaces/server';
|
|
19
|
-
import type
|
|
21
|
+
import { type L1ToL2MessageSource, computeCheckpointOutHash } from '@aztec/stdlib/messaging';
|
|
20
22
|
import { OffenseType, getOffenseTypeName } from '@aztec/stdlib/slashing';
|
|
23
|
+
import type { CheckpointGlobalVariables } from '@aztec/stdlib/tx';
|
|
21
24
|
import {
|
|
22
25
|
ReExFailedTxsError,
|
|
23
26
|
ReExStateMismatchError,
|
|
@@ -52,7 +55,7 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
52
55
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
53
56
|
private epochCache: EpochCache,
|
|
54
57
|
private txProvider: Pick<ITxProvider, 'getAvailableTxs'>,
|
|
55
|
-
private
|
|
58
|
+
private checkpointsBuilder: ICheckpointsBuilder,
|
|
56
59
|
penalties: EpochPruneWatcherPenalties,
|
|
57
60
|
) {
|
|
58
61
|
super();
|
|
@@ -63,12 +66,12 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
public start() {
|
|
66
|
-
this.l2BlockSource.on(L2BlockSourceEvents.
|
|
69
|
+
this.l2BlockSource.events.on(L2BlockSourceEvents.L2PruneUnproven, this.boundHandlePruneL2Blocks);
|
|
67
70
|
return Promise.resolve();
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
public stop() {
|
|
71
|
-
this.l2BlockSource.removeListener(L2BlockSourceEvents.
|
|
74
|
+
this.l2BlockSource.events.removeListener(L2BlockSourceEvents.L2PruneUnproven, this.boundHandlePruneL2Blocks);
|
|
72
75
|
return Promise.resolve();
|
|
73
76
|
}
|
|
74
77
|
|
|
@@ -77,7 +80,7 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
77
80
|
this.log.verbose('EpochPruneWatcher config updated', this.penalties);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
private handlePruneL2Blocks(event:
|
|
83
|
+
private handlePruneL2Blocks(event: L2PruneUnprovenEvent): void {
|
|
81
84
|
const { blocks, epochNumber } = event;
|
|
82
85
|
void this.processPruneL2Blocks(blocks, epochNumber).catch(err =>
|
|
83
86
|
this.log.error('Error processing pruned L2 blocks', err, { epochNumber }),
|
|
@@ -98,13 +101,13 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
98
101
|
private async processPruneL2Blocks(blocks: L2Block[], epochNumber: EpochNumber): Promise<void> {
|
|
99
102
|
try {
|
|
100
103
|
const l1Constants = this.epochCache.getL1Constants();
|
|
101
|
-
const epochBlocks = blocks.filter(b => getEpochAtSlot(b.
|
|
104
|
+
const epochBlocks = blocks.filter(b => getEpochAtSlot(b.header.getSlot(), l1Constants) === epochNumber);
|
|
102
105
|
this.log.info(
|
|
103
106
|
`Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`,
|
|
104
107
|
{ blocks: epochBlocks.map(b => b.toBlockInfo()) },
|
|
105
108
|
);
|
|
106
109
|
|
|
107
|
-
await this.validateBlocks(epochBlocks);
|
|
110
|
+
await this.validateBlocks(epochBlocks, epochNumber);
|
|
108
111
|
this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
|
|
109
112
|
await this.emitSlashForEpoch(OffenseType.VALID_EPOCH_PRUNED, epochNumber);
|
|
110
113
|
} catch (error) {
|
|
@@ -119,21 +122,83 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
public async validateBlocks(blocks: L2Block[]): Promise<void> {
|
|
125
|
+
public async validateBlocks(blocks: L2Block[], epochNumber: EpochNumber): Promise<void> {
|
|
123
126
|
if (blocks.length === 0) {
|
|
124
127
|
return;
|
|
125
128
|
}
|
|
126
|
-
|
|
129
|
+
|
|
130
|
+
// Sort blocks by block number and group by checkpoint
|
|
131
|
+
const sortedBlocks = [...blocks].sort((a, b) => a.number - b.number);
|
|
132
|
+
const blocksByCheckpoint = chunkBy(sortedBlocks, b => b.checkpointNumber);
|
|
133
|
+
|
|
134
|
+
// Get prior checkpoints in the epoch (in case this was a partial prune) to extract the out hashes
|
|
135
|
+
const priorCheckpointOutHashes = (await this.l2BlockSource.getCheckpointsDataForEpoch(epochNumber))
|
|
136
|
+
.filter(c => c.checkpointNumber < sortedBlocks[0].checkpointNumber)
|
|
137
|
+
.map(c => c.checkpointOutHash);
|
|
138
|
+
let previousCheckpointOutHashes: Fr[] = [...priorCheckpointOutHashes];
|
|
139
|
+
|
|
140
|
+
const fork = await this.checkpointsBuilder.getFork(
|
|
141
|
+
BlockNumber(sortedBlocks[0].header.globalVariables.blockNumber - 1),
|
|
142
|
+
);
|
|
127
143
|
try {
|
|
128
|
-
for (const
|
|
129
|
-
await this.
|
|
144
|
+
for (const checkpointBlocks of blocksByCheckpoint) {
|
|
145
|
+
await this.validateCheckpoint(checkpointBlocks, previousCheckpointOutHashes, fork);
|
|
146
|
+
|
|
147
|
+
// Compute checkpoint out hash from all blocks in this checkpoint
|
|
148
|
+
const checkpointOutHash = computeCheckpointOutHash(
|
|
149
|
+
checkpointBlocks.map(b => b.body.txEffects.map(tx => tx.l2ToL1Msgs)),
|
|
150
|
+
);
|
|
151
|
+
previousCheckpointOutHashes = [...previousCheckpointOutHashes, checkpointOutHash];
|
|
130
152
|
}
|
|
131
153
|
} finally {
|
|
132
154
|
await fork.close();
|
|
133
155
|
}
|
|
134
156
|
}
|
|
135
157
|
|
|
136
|
-
|
|
158
|
+
private async validateCheckpoint(
|
|
159
|
+
checkpointBlocks: L2Block[],
|
|
160
|
+
previousCheckpointOutHashes: Fr[],
|
|
161
|
+
fork: MerkleTreeWriteOperations,
|
|
162
|
+
): Promise<void> {
|
|
163
|
+
const checkpointNumber = checkpointBlocks[0].checkpointNumber;
|
|
164
|
+
this.log.debug(`Validating pruned checkpoint ${checkpointNumber} with ${checkpointBlocks.length} blocks`);
|
|
165
|
+
|
|
166
|
+
// Get L1ToL2Messages once for the entire checkpoint
|
|
167
|
+
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
168
|
+
|
|
169
|
+
// Build checkpoint constants from first block's global variables
|
|
170
|
+
const gv = checkpointBlocks[0].header.globalVariables;
|
|
171
|
+
const constants: CheckpointGlobalVariables = {
|
|
172
|
+
chainId: gv.chainId,
|
|
173
|
+
version: gv.version,
|
|
174
|
+
slotNumber: gv.slotNumber,
|
|
175
|
+
timestamp: gv.timestamp,
|
|
176
|
+
coinbase: gv.coinbase,
|
|
177
|
+
feeRecipient: gv.feeRecipient,
|
|
178
|
+
gasFees: gv.gasFees,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// Start checkpoint builder once for all blocks in this checkpoint
|
|
182
|
+
const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(
|
|
183
|
+
checkpointNumber,
|
|
184
|
+
constants,
|
|
185
|
+
0n, // feeAssetPriceModifier is not used for validation of the checkpoint content
|
|
186
|
+
l1ToL2Messages,
|
|
187
|
+
previousCheckpointOutHashes,
|
|
188
|
+
fork,
|
|
189
|
+
this.log.getBindings(),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// Validate all blocks in the checkpoint sequentially
|
|
193
|
+
for (const block of checkpointBlocks) {
|
|
194
|
+
await this.validateBlockInCheckpoint(block, checkpointBuilder);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private async validateBlockInCheckpoint(
|
|
199
|
+
blockFromL1: L2Block,
|
|
200
|
+
checkpointBuilder: ICheckpointBlockBuilder,
|
|
201
|
+
): Promise<void> {
|
|
137
202
|
this.log.debug(`Validating pruned block ${blockFromL1.header.globalVariables.blockNumber}`);
|
|
138
203
|
const txHashes = blockFromL1.body.txEffects.map(txEffect => txEffect.txHash);
|
|
139
204
|
// We load txs from the mempool directly, since the TxCollector running in the background has already been
|
|
@@ -145,15 +210,9 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
145
210
|
throw new TransactionsNotAvailableError(missingTxs);
|
|
146
211
|
}
|
|
147
212
|
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
txs,
|
|
152
|
-
l1ToL2Messages,
|
|
153
|
-
blockFromL1.header.globalVariables,
|
|
154
|
-
{},
|
|
155
|
-
fork,
|
|
156
|
-
);
|
|
213
|
+
const gv = blockFromL1.header.globalVariables;
|
|
214
|
+
const { block, failedTxs, numTxs } = await checkpointBuilder.buildBlock(txs, gv.blockNumber, gv.timestamp, {});
|
|
215
|
+
|
|
157
216
|
if (numTxs !== txs.length) {
|
|
158
217
|
// This should be detected by state mismatch, but this makes it easier to debug.
|
|
159
218
|
throw new ValidatorError(`Built block with ${numTxs} txs, expected ${txs.length}`);
|