@aztec/slasher 2.0.3 → 2.1.0-rc.2
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 +7 -1
- package/dest/empire_slasher_client.d.ts.map +1 -1
- package/dest/empire_slasher_client.js +6 -1
- package/dest/tally_slasher_client.d.ts +10 -2
- package/dest/tally_slasher_client.d.ts.map +1 -1
- package/dest/tally_slasher_client.js +41 -8
- package/dest/watchers/epoch_prune_watcher.d.ts +2 -0
- package/dest/watchers/epoch_prune_watcher.d.ts.map +1 -1
- package/dest/watchers/epoch_prune_watcher.js +28 -31
- package/package.json +9 -9
- package/src/config.ts +7 -1
- package/src/empire_slasher_client.ts +7 -1
- package/src/tally_slasher_client.ts +49 -9
- package/src/watchers/epoch_prune_watcher.ts +41 -46
package/dest/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAQnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,eAAO,MAAM,oBAAoB,EAAE,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAQnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,eAAO,MAAM,oBAAoB,EAAE,aAoBlC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,kBAAkB,CAAC,aAAa,CAyHnE,CAAC"}
|
package/dest/config.js
CHANGED
|
@@ -19,6 +19,7 @@ export const DefaultSlasherConfig = {
|
|
|
19
19
|
slashOffenseExpirationRounds: 4,
|
|
20
20
|
slashMaxPayloadSize: 50,
|
|
21
21
|
slashGracePeriodL2Slots: 0,
|
|
22
|
+
slashExecuteRoundsLookBack: 4,
|
|
22
23
|
slashSelfAllowed: false
|
|
23
24
|
};
|
|
24
25
|
export const slasherConfigMappings = {
|
|
@@ -62,7 +63,7 @@ export const slasherConfigMappings = {
|
|
|
62
63
|
},
|
|
63
64
|
slashBroadcastedInvalidBlockPenalty: {
|
|
64
65
|
env: 'SLASH_INVALID_BLOCK_PENALTY',
|
|
65
|
-
description: 'Penalty amount for slashing a validator for an invalid block.',
|
|
66
|
+
description: 'Penalty amount for slashing a validator for an invalid block proposed via p2p.',
|
|
66
67
|
...bigintConfigHelper(DefaultSlasherConfig.slashBroadcastedInvalidBlockPenalty)
|
|
67
68
|
},
|
|
68
69
|
slashInactivityTargetPercentage: {
|
|
@@ -121,6 +122,11 @@ export const slasherConfigMappings = {
|
|
|
121
122
|
env: 'SLASH_GRACE_PERIOD_L2_SLOTS',
|
|
122
123
|
...numberConfigHelper(DefaultSlasherConfig.slashGracePeriodL2Slots)
|
|
123
124
|
},
|
|
125
|
+
slashExecuteRoundsLookBack: {
|
|
126
|
+
env: 'SLASH_EXECUTE_ROUNDS_LOOK_BACK',
|
|
127
|
+
description: 'How many rounds to look back when searching for a round to execute.',
|
|
128
|
+
...numberConfigHelper(DefaultSlasherConfig.slashExecuteRoundsLookBack)
|
|
129
|
+
},
|
|
124
130
|
slashSelfAllowed: {
|
|
125
131
|
description: 'Whether to allow slashes to own validators',
|
|
126
132
|
...booleanConfigHelper(DefaultSlasherConfig.slashSelfAllowed)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empire_slasher_client.d.ts","sourceRoot":"","sources":["../src/empire_slasher_client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlG,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAG3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACL,KAAK,OAAO,EAGZ,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,EAChC,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAQvB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAA0B,KAAK,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAE5G,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,gDAAgD;AAChD,MAAM,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAEhD,qGAAqG;AACrG,MAAM,MAAM,qBAAqB,GAAG;IAClC,8BAA8B,EAAE,MAAM,CAAC;IACvC,+BAA+B,EAAE,MAAM,CAAC;IACxC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GAAG,IAAI,CACN,iBAAiB,EACjB,eAAe,GAAG,uBAAuB,GAAG,eAAe,GAAG,cAAc,GAAG,cAAc,GAAG,sBAAsB,CACvH,GACC,8BAA8B,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,qBAAa,mBAAoB,YAAW,2BAA2B,EAAE,sBAAsB;IAS3F,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,oBAAoB;IAC5B,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IAEd,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,GAAG;IAlBb,SAAS,CAAC,kBAAkB,EAAE,gBAAgB,EAAE,CAAM;IAEtD,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,YAAY,CAAoB;gBAG9B,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,qBAAqB,EAC/B,oBAAoB,EAAE,oBAAoB,EAC1C,gBAAgB,EAAE,8BAA8B,EAChD,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,cAAc,EAC9B,QAAQ,EAAE,OAAO,EAAE,EACX,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,oBAAoB,EACnC,aAAa,EAAE,oBAAoB,EACnC,GAAG,yCAAiC;IAOjC,KAAK;IA+ClB;;;OAGG;IACU,IAAI;IAoBjB,iCAAiC;IAC1B,SAAS,IAAI,mBAAmB;IAIvC;;;OAGG;IACI,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC;IAU3C,gBAAgB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAIvD;;;OAGG;cACa,cAAc,CAAC,KAAK,EAAE,MAAM;IAM5C;;;OAGG;cACa,wBAAwB,CAAC,cAAc,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM;IA8BlF;;;OAGG;IACH,SAAS,CAAC,sBAAsB,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM;IASnE;;;;OAIG;cACa,uBAAuB,CAAC,cAAc,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU;IAsBxG;;;;OAIG;IACU,sBAAsB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAoBvE,0CAA0C;IACnC,kBAAkB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAI/C,uFAAuF;cACvE,yCAAyC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAU5F;;;;;OAKG;IACH,SAAS,CAAC,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,SAAS,CAAC,GAAG,MAAM;IAK9F;;;;OAIG;IACU,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IASnF,uFAAuF;cACvE,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"empire_slasher_client.d.ts","sourceRoot":"","sources":["../src/empire_slasher_client.ts"],"names":[],"mappings":"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"}
|
|
@@ -311,10 +311,15 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
311
311
|
toRemove.push(payload);
|
|
312
312
|
continue;
|
|
313
313
|
}
|
|
314
|
+
// Check if slashing is enabled at all
|
|
315
|
+
if (!await this.slasher.isSlashingEnabled()) {
|
|
316
|
+
this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`);
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
314
319
|
// Check if the slash payload is vetoed
|
|
315
320
|
const isVetoed = await this.slasher.isPayloadVetoed(payload.payload);
|
|
316
321
|
if (isVetoed) {
|
|
317
|
-
this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed
|
|
322
|
+
this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed (skipping execution)`);
|
|
318
323
|
toRemove.push(payload);
|
|
319
324
|
continue;
|
|
320
325
|
}
|
|
@@ -21,7 +21,7 @@ export type TallySlasherSettings = Prettify<SlashRoundMonitorSettings & SlashOff
|
|
|
21
21
|
/** Committee size for block proposal */
|
|
22
22
|
targetCommitteeSize: number;
|
|
23
23
|
}>;
|
|
24
|
-
export type TallySlasherClientConfig = SlashOffensesCollectorConfig & Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever'>;
|
|
24
|
+
export type TallySlasherClientConfig = SlashOffensesCollectorConfig & Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever' | 'slashExecuteRoundsLookBack'>;
|
|
25
25
|
/**
|
|
26
26
|
* The Tally Slasher client is responsible for managing slashable offenses using
|
|
27
27
|
* the consensus-based slashing model where proposers vote on individual validator offenses.
|
|
@@ -87,8 +87,16 @@ export declare class TallySlasherClient implements ProposerSlashActionProvider,
|
|
|
87
87
|
* @returns The actions to take
|
|
88
88
|
*/
|
|
89
89
|
getProposerActions(slotNumber: bigint): Promise<ProposerSlashAction[]>;
|
|
90
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Returns an execute slash action if there are any rounds ready to be executed.
|
|
92
|
+
* Returns the oldest slash action if there are multiple rounds pending execution.
|
|
93
|
+
*/
|
|
91
94
|
protected getExecuteSlashAction(slotNumber: bigint): Promise<ProposerSlashAction | undefined>;
|
|
95
|
+
/**
|
|
96
|
+
* Checks if a given round is executable and returns an execute-slash action for it if so.
|
|
97
|
+
* Assumes round number has already been checked against lifetime and execution delay.
|
|
98
|
+
*/
|
|
99
|
+
private tryGetRoundExecuteAction;
|
|
92
100
|
/** Returns a vote action based on offenses from the target round (with offset applied) */
|
|
93
101
|
protected getVoteOffensesAction(slotNumber: bigint): Promise<ProposerSlashAction | undefined>;
|
|
94
102
|
/** Returns the committees that were active during the timespan of a given round */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tally_slasher_client.d.ts","sourceRoot":"","sources":["../src/tally_slasher_client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"tally_slasher_client.d.ts","sourceRoot":"","sources":["../src/tally_slasher_client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/aztec.js';
|
|
2
|
+
import { maxBigint } from '@aztec/foundation/bigint';
|
|
2
3
|
import { compactArray, partition, times } from '@aztec/foundation/collection';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import { sleep } from '@aztec/foundation/sleep';
|
|
@@ -120,19 +121,53 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
120
121
|
voteAction
|
|
121
122
|
]);
|
|
122
123
|
}
|
|
123
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Returns an execute slash action if there are any rounds ready to be executed.
|
|
126
|
+
* Returns the oldest slash action if there are multiple rounds pending execution.
|
|
127
|
+
*/ async getExecuteSlashAction(slotNumber) {
|
|
124
128
|
const { round: currentRound } = this.roundMonitor.getRoundForSlot(slotNumber);
|
|
125
129
|
const slashingExecutionDelayInRounds = BigInt(this.settings.slashingExecutionDelayInRounds);
|
|
126
130
|
const executableRound = currentRound - slashingExecutionDelayInRounds - 1n;
|
|
127
|
-
|
|
131
|
+
const lookBack = BigInt(this.config.slashExecuteRoundsLookBack);
|
|
132
|
+
const oldestExecutableRound = maxBigint(0n, executableRound - lookBack);
|
|
133
|
+
// Check if slashing is enabled at all
|
|
134
|
+
if (!await this.slasher.isSlashingEnabled()) {
|
|
135
|
+
this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`);
|
|
128
136
|
return undefined;
|
|
129
137
|
}
|
|
130
|
-
|
|
138
|
+
this.log.debug(`Checking slashing rounds ${oldestExecutableRound} to ${executableRound} to execute`, {
|
|
139
|
+
slotNumber,
|
|
131
140
|
currentRound,
|
|
141
|
+
oldestExecutableRound,
|
|
132
142
|
executableRound,
|
|
133
|
-
|
|
143
|
+
slashingExecutionDelayInRounds,
|
|
144
|
+
lookBack,
|
|
145
|
+
slashingLifetimeInRounds: this.settings.slashingLifetimeInRounds
|
|
146
|
+
});
|
|
147
|
+
// Iterate over all rounds, starting from the oldest, until we find one that is executable
|
|
148
|
+
for(let roundToCheck = oldestExecutableRound; roundToCheck <= executableRound; roundToCheck++){
|
|
149
|
+
const action = await this.tryGetRoundExecuteAction(roundToCheck);
|
|
150
|
+
if (action) {
|
|
151
|
+
return action;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// And return nothing if none are found
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Checks if a given round is executable and returns an execute-slash action for it if so.
|
|
159
|
+
* Assumes round number has already been checked against lifetime and execution delay.
|
|
160
|
+
*/ async tryGetRoundExecuteAction(executableRound) {
|
|
161
|
+
let logData = {
|
|
162
|
+
executableRound
|
|
134
163
|
};
|
|
164
|
+
this.log.debug(`Testing if slashing round ${executableRound} is executable`, logData);
|
|
135
165
|
try {
|
|
166
|
+
// Check if slashing is enabled at all
|
|
167
|
+
if (!await this.slasher.isSlashingEnabled()) {
|
|
168
|
+
this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`, logData);
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
136
171
|
const roundInfo = await this.tallySlashingProposer.getRound(executableRound);
|
|
137
172
|
logData = {
|
|
138
173
|
...logData,
|
|
@@ -141,9 +176,6 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
141
176
|
if (roundInfo.isExecuted) {
|
|
142
177
|
this.log.verbose(`Round ${executableRound} has already been executed`, logData);
|
|
143
178
|
return undefined;
|
|
144
|
-
} else if (!roundInfo.readyToExecute) {
|
|
145
|
-
this.log.verbose(`Round ${executableRound} is not ready to execute yet`, logData);
|
|
146
|
-
return undefined;
|
|
147
179
|
} else if (roundInfo.voteCount === 0n) {
|
|
148
180
|
this.log.debug(`Round ${executableRound} received no votes`, logData);
|
|
149
181
|
return undefined;
|
|
@@ -151,6 +183,7 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
151
183
|
this.log.verbose(`Round ${executableRound} does not have enough votes to execute`, logData);
|
|
152
184
|
return undefined;
|
|
153
185
|
}
|
|
186
|
+
// Check if the round yields any slashing at all
|
|
154
187
|
const { actions: slashActions, committees } = await this.tallySlashingProposer.getTally(executableRound);
|
|
155
188
|
if (slashActions.length === 0) {
|
|
156
189
|
this.log.verbose(`Round ${executableRound} does not resolve in any slashing`, logData);
|
|
@@ -184,8 +217,8 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
184
217
|
};
|
|
185
218
|
} catch (error) {
|
|
186
219
|
this.log.error(`Error checking round to execute ${executableRound}`, error);
|
|
220
|
+
return undefined;
|
|
187
221
|
}
|
|
188
|
-
return undefined;
|
|
189
222
|
}
|
|
190
223
|
/** Returns a vote action based on offenses from the target round (with offset applied) */ async getVoteOffensesAction(slotNumber) {
|
|
191
224
|
// Compute what round we are in based on the slot number and what round will be slashed
|
|
@@ -26,6 +26,8 @@ export declare class EpochPruneWatcher extends EpochPruneWatcher_base implements
|
|
|
26
26
|
stop(): Promise<void>;
|
|
27
27
|
updateConfig(config: Partial<SlasherConfig>): void;
|
|
28
28
|
private handlePruneL2Blocks;
|
|
29
|
+
private emitSlashForEpoch;
|
|
30
|
+
private processPruneL2Blocks;
|
|
29
31
|
validateBlocks(blocks: L2Block[]): Promise<void>;
|
|
30
32
|
validateBlock(blockFromL1: L2Block, fork: MerkleTreeWriteOperations): Promise<void>;
|
|
31
33
|
private getValidatorsForEpoch;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,EAEL,OAAO,EAEP,KAAK,yBAAyB,EAE/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,qBAAqB,EACrB,WAAW,EACX,yBAAyB,EACzB,aAAa,EACd,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAWnE,OAAO,EAA6C,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7G,QAAA,MAAM,oCAAoC,+DAAgE,CAAC;AAE3G,KAAK,0BAA0B,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,oCAAoC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;sCAQrD,UAAU,cAAc;AANhF;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,sBAA2C,YAAW,OAAO;IAShG,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,YAAY;IAZtB,OAAO,CAAC,GAAG,CAA+C;IAG1D,OAAO,CAAC,wBAAwB,CAAuC;IAEvE,OAAO,CAAC,SAAS,CAA6B;gBAGpC,aAAa,EAAE,yBAAyB,EACxC,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAChD,YAAY,EAAE,qBAAqB,EAC3C,SAAS,EAAE,0BAA0B;IAShC,KAAK;IAKL,IAAI;IAKJ,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI;IAKzD,OAAO,CAAC,mBAAmB;YAOb,iBAAiB;YAWjB,oBAAoB;IAwBrB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAchD,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;YAgClF,qBAAqB;IASnC,OAAO,CAAC,wBAAwB;CAgBjC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { merge, pick } from '@aztec/foundation/collection';
|
|
2
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
3
|
import { L2BlockSourceEvents } from '@aztec/stdlib/block';
|
|
4
|
-
import {
|
|
4
|
+
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
5
|
+
import { OffenseType, getOffenseTypeName } from '@aztec/stdlib/slashing';
|
|
5
6
|
import { ReExFailedTxsError, ReExStateMismatchError, TransactionsNotAvailableError, ValidatorError } from '@aztec/stdlib/validators';
|
|
6
7
|
import EventEmitter from 'node:events';
|
|
7
8
|
import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
@@ -43,44 +44,40 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
|
|
|
43
44
|
}
|
|
44
45
|
handlePruneL2Blocks(event) {
|
|
45
46
|
const { blocks, epochNumber } = event;
|
|
46
|
-
this.log.
|
|
47
|
-
|
|
47
|
+
void this.processPruneL2Blocks(blocks, epochNumber).catch((err)=>this.log.error('Error processing pruned L2 blocks', err, {
|
|
48
|
+
epochNumber
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
async emitSlashForEpoch(offense, epochNumber) {
|
|
52
|
+
const validators = await this.getValidatorsForEpoch(epochNumber);
|
|
53
|
+
if (validators.length === 0) {
|
|
54
|
+
this.log.warn(`No validators found for epoch ${epochNumber} (cannot slash for ${getOffenseTypeName(offense)})`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const args = this.validatorsToSlashingArgs(validators, offense, BigInt(epochNumber));
|
|
58
|
+
this.log.verbose(`Created slash for ${getOffenseTypeName(offense)} at epoch ${epochNumber}`, args);
|
|
59
|
+
this.emit(WANT_TO_SLASH_EVENT, args);
|
|
60
|
+
}
|
|
61
|
+
async processPruneL2Blocks(blocks, epochNumber) {
|
|
62
|
+
try {
|
|
63
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
64
|
+
const epochBlocks = blocks.filter((b)=>getEpochAtSlot(b.slot, l1Constants) === epochNumber);
|
|
65
|
+
this.log.info(`Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`, {
|
|
66
|
+
blocks: epochBlocks.map((b)=>b.toBlockInfo())
|
|
67
|
+
});
|
|
68
|
+
await this.validateBlocks(epochBlocks);
|
|
48
69
|
this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const result = {
|
|
52
|
-
validators,
|
|
53
|
-
offense: OffenseType.VALID_EPOCH_PRUNED
|
|
54
|
-
};
|
|
55
|
-
return result;
|
|
56
|
-
}).catch(async (error)=>{
|
|
70
|
+
await this.emitSlashForEpoch(OffenseType.VALID_EPOCH_PRUNED, epochNumber);
|
|
71
|
+
} catch (error) {
|
|
57
72
|
if (error instanceof TransactionsNotAvailableError) {
|
|
58
73
|
this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, {
|
|
59
74
|
message: error.message
|
|
60
75
|
});
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
validators,
|
|
64
|
-
offense: OffenseType.DATA_WITHHOLDING
|
|
65
|
-
};
|
|
76
|
+
await this.emitSlashForEpoch(OffenseType.DATA_WITHHOLDING, epochNumber);
|
|
66
77
|
} else {
|
|
67
78
|
this.log.error(`Error while validating pruned epoch ${epochNumber}. Will not want to slash.`, error);
|
|
68
|
-
return {
|
|
69
|
-
validators: [],
|
|
70
|
-
offense: undefined
|
|
71
|
-
};
|
|
72
79
|
}
|
|
73
|
-
}
|
|
74
|
-
if (validators.length === 0 || offense === undefined) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const args = this.validatorsToSlashingArgs(validators, offense, BigInt(epochNumber));
|
|
78
|
-
this.log.info(`Slash for epoch ${epochNumber} created`, args);
|
|
79
|
-
this.emit(WANT_TO_SLASH_EVENT, args);
|
|
80
|
-
}).catch((error)=>{
|
|
81
|
-
// This can happen if we fail to get the validators for the epoch.
|
|
82
|
-
this.log.error('Error while creating slash for epoch', error);
|
|
83
|
-
});
|
|
80
|
+
}
|
|
84
81
|
}
|
|
85
82
|
async validateBlocks(blocks) {
|
|
86
83
|
if (blocks.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/slasher",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.1.0-rc.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -54,20 +54,20 @@
|
|
|
54
54
|
]
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@aztec/epoch-cache": "2.0.
|
|
58
|
-
"@aztec/ethereum": "2.0.
|
|
59
|
-
"@aztec/foundation": "2.0.
|
|
60
|
-
"@aztec/kv-store": "2.0.
|
|
61
|
-
"@aztec/l1-artifacts": "2.0.
|
|
62
|
-
"@aztec/stdlib": "2.0.
|
|
63
|
-
"@aztec/telemetry-client": "2.0.
|
|
57
|
+
"@aztec/epoch-cache": "2.1.0-rc.2",
|
|
58
|
+
"@aztec/ethereum": "2.1.0-rc.2",
|
|
59
|
+
"@aztec/foundation": "2.1.0-rc.2",
|
|
60
|
+
"@aztec/kv-store": "2.1.0-rc.2",
|
|
61
|
+
"@aztec/l1-artifacts": "2.1.0-rc.2",
|
|
62
|
+
"@aztec/stdlib": "2.1.0-rc.2",
|
|
63
|
+
"@aztec/telemetry-client": "2.1.0-rc.2",
|
|
64
64
|
"source-map-support": "^0.5.21",
|
|
65
65
|
"tslib": "^2.4.0",
|
|
66
66
|
"viem": "2.23.7",
|
|
67
67
|
"zod": "^3.23.8"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@aztec/aztec.js": "2.0.
|
|
70
|
+
"@aztec/aztec.js": "2.1.0-rc.2",
|
|
71
71
|
"@jest/globals": "^30.0.0",
|
|
72
72
|
"@types/jest": "^30.0.0",
|
|
73
73
|
"@types/node": "^22.15.17",
|
package/src/config.ts
CHANGED
|
@@ -29,6 +29,7 @@ export const DefaultSlasherConfig: SlasherConfig = {
|
|
|
29
29
|
slashOffenseExpirationRounds: 4,
|
|
30
30
|
slashMaxPayloadSize: 50,
|
|
31
31
|
slashGracePeriodL2Slots: 0,
|
|
32
|
+
slashExecuteRoundsLookBack: 4,
|
|
32
33
|
slashSelfAllowed: false,
|
|
33
34
|
};
|
|
34
35
|
|
|
@@ -83,7 +84,7 @@ export const slasherConfigMappings: ConfigMappingsType<SlasherConfig> = {
|
|
|
83
84
|
},
|
|
84
85
|
slashBroadcastedInvalidBlockPenalty: {
|
|
85
86
|
env: 'SLASH_INVALID_BLOCK_PENALTY',
|
|
86
|
-
description: 'Penalty amount for slashing a validator for an invalid block.',
|
|
87
|
+
description: 'Penalty amount for slashing a validator for an invalid block proposed via p2p.',
|
|
87
88
|
...bigintConfigHelper(DefaultSlasherConfig.slashBroadcastedInvalidBlockPenalty),
|
|
88
89
|
},
|
|
89
90
|
slashInactivityTargetPercentage: {
|
|
@@ -144,6 +145,11 @@ export const slasherConfigMappings: ConfigMappingsType<SlasherConfig> = {
|
|
|
144
145
|
env: 'SLASH_GRACE_PERIOD_L2_SLOTS',
|
|
145
146
|
...numberConfigHelper(DefaultSlasherConfig.slashGracePeriodL2Slots),
|
|
146
147
|
},
|
|
148
|
+
slashExecuteRoundsLookBack: {
|
|
149
|
+
env: 'SLASH_EXECUTE_ROUNDS_LOOK_BACK',
|
|
150
|
+
description: 'How many rounds to look back when searching for a round to execute.',
|
|
151
|
+
...numberConfigHelper(DefaultSlasherConfig.slashExecuteRoundsLookBack),
|
|
152
|
+
},
|
|
147
153
|
slashSelfAllowed: {
|
|
148
154
|
description: 'Whether to allow slashes to own validators',
|
|
149
155
|
...booleanConfigHelper(DefaultSlasherConfig.slashSelfAllowed),
|
|
@@ -404,11 +404,17 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
|
|
|
404
404
|
continue;
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
+
// Check if slashing is enabled at all
|
|
408
|
+
if (!(await this.slasher.isSlashingEnabled())) {
|
|
409
|
+
this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`);
|
|
410
|
+
return undefined;
|
|
411
|
+
}
|
|
412
|
+
|
|
407
413
|
// Check if the slash payload is vetoed
|
|
408
414
|
const isVetoed = await this.slasher.isPayloadVetoed(payload.payload);
|
|
409
415
|
|
|
410
416
|
if (isVetoed) {
|
|
411
|
-
this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed
|
|
417
|
+
this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed (skipping execution)`);
|
|
412
418
|
toRemove.push(payload);
|
|
413
419
|
continue;
|
|
414
420
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/aztec.js';
|
|
2
2
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
3
3
|
import { RollupContract, SlasherContract, TallySlashingProposerContract } from '@aztec/ethereum/contracts';
|
|
4
|
+
import { maxBigint } from '@aztec/foundation/bigint';
|
|
4
5
|
import { compactArray, partition, times } from '@aztec/foundation/collection';
|
|
5
6
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
7
|
import { sleep } from '@aztec/foundation/sleep';
|
|
@@ -45,7 +46,7 @@ export type TallySlasherSettings = Prettify<
|
|
|
45
46
|
>;
|
|
46
47
|
|
|
47
48
|
export type TallySlasherClientConfig = SlashOffensesCollectorConfig &
|
|
48
|
-
Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever'>;
|
|
49
|
+
Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever' | 'slashExecuteRoundsLookBack'>;
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
52
|
* The Tally Slasher client is responsible for managing slashable offenses using
|
|
@@ -177,26 +178,65 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
|
|
|
177
178
|
return compactArray<ProposerSlashAction>([executeAction, voteAction]);
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
/**
|
|
181
|
+
/**
|
|
182
|
+
* Returns an execute slash action if there are any rounds ready to be executed.
|
|
183
|
+
* Returns the oldest slash action if there are multiple rounds pending execution.
|
|
184
|
+
*/
|
|
181
185
|
protected async getExecuteSlashAction(slotNumber: bigint): Promise<ProposerSlashAction | undefined> {
|
|
182
186
|
const { round: currentRound } = this.roundMonitor.getRoundForSlot(slotNumber);
|
|
183
187
|
const slashingExecutionDelayInRounds = BigInt(this.settings.slashingExecutionDelayInRounds);
|
|
184
188
|
const executableRound = currentRound - slashingExecutionDelayInRounds - 1n;
|
|
185
|
-
|
|
189
|
+
const lookBack = BigInt(this.config.slashExecuteRoundsLookBack);
|
|
190
|
+
const oldestExecutableRound = maxBigint(0n, executableRound - lookBack);
|
|
191
|
+
|
|
192
|
+
// Check if slashing is enabled at all
|
|
193
|
+
if (!(await this.slasher.isSlashingEnabled())) {
|
|
194
|
+
this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`);
|
|
186
195
|
return undefined;
|
|
187
196
|
}
|
|
188
197
|
|
|
189
|
-
|
|
198
|
+
this.log.debug(`Checking slashing rounds ${oldestExecutableRound} to ${executableRound} to execute`, {
|
|
199
|
+
slotNumber,
|
|
200
|
+
currentRound,
|
|
201
|
+
oldestExecutableRound,
|
|
202
|
+
executableRound,
|
|
203
|
+
slashingExecutionDelayInRounds,
|
|
204
|
+
lookBack,
|
|
205
|
+
slashingLifetimeInRounds: this.settings.slashingLifetimeInRounds,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Iterate over all rounds, starting from the oldest, until we find one that is executable
|
|
209
|
+
for (let roundToCheck = oldestExecutableRound; roundToCheck <= executableRound; roundToCheck++) {
|
|
210
|
+
const action = await this.tryGetRoundExecuteAction(roundToCheck);
|
|
211
|
+
if (action) {
|
|
212
|
+
return action;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// And return nothing if none are found
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Checks if a given round is executable and returns an execute-slash action for it if so.
|
|
222
|
+
* Assumes round number has already been checked against lifetime and execution delay.
|
|
223
|
+
*/
|
|
224
|
+
private async tryGetRoundExecuteAction(executableRound: bigint): Promise<ProposerSlashAction | undefined> {
|
|
225
|
+
let logData: Record<string, unknown> = { executableRound };
|
|
226
|
+
this.log.debug(`Testing if slashing round ${executableRound} is executable`, logData);
|
|
190
227
|
|
|
191
228
|
try {
|
|
229
|
+
// Check if slashing is enabled at all
|
|
230
|
+
if (!(await this.slasher.isSlashingEnabled())) {
|
|
231
|
+
this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`, logData);
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
|
|
192
235
|
const roundInfo = await this.tallySlashingProposer.getRound(executableRound);
|
|
193
236
|
logData = { ...logData, roundInfo };
|
|
194
237
|
if (roundInfo.isExecuted) {
|
|
195
238
|
this.log.verbose(`Round ${executableRound} has already been executed`, logData);
|
|
196
239
|
return undefined;
|
|
197
|
-
} else if (!roundInfo.readyToExecute) {
|
|
198
|
-
this.log.verbose(`Round ${executableRound} is not ready to execute yet`, logData);
|
|
199
|
-
return undefined;
|
|
200
240
|
} else if (roundInfo.voteCount === 0n) {
|
|
201
241
|
this.log.debug(`Round ${executableRound} received no votes`, logData);
|
|
202
242
|
return undefined;
|
|
@@ -205,6 +245,7 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
|
|
|
205
245
|
return undefined;
|
|
206
246
|
}
|
|
207
247
|
|
|
248
|
+
// Check if the round yields any slashing at all
|
|
208
249
|
const { actions: slashActions, committees } = await this.tallySlashingProposer.getTally(executableRound);
|
|
209
250
|
if (slashActions.length === 0) {
|
|
210
251
|
this.log.verbose(`Round ${executableRound} does not resolve in any slashing`, logData);
|
|
@@ -239,9 +280,8 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
|
|
|
239
280
|
return { type: 'execute-slash', round: executableRound, committees: slashedCommittees };
|
|
240
281
|
} catch (error) {
|
|
241
282
|
this.log.error(`Error checking round to execute ${executableRound}`, error);
|
|
283
|
+
return undefined;
|
|
242
284
|
}
|
|
243
|
-
|
|
244
|
-
return undefined;
|
|
245
285
|
}
|
|
246
286
|
|
|
247
287
|
/** Returns a vote action based on offenses from the target round (with offset applied) */
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Tx } from '@aztec/aztec.js';
|
|
2
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
3
2
|
import { merge, pick } from '@aztec/foundation/collection';
|
|
4
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
type L2BlockSourceEventEmitter,
|
|
10
9
|
L2BlockSourceEvents,
|
|
11
10
|
} from '@aztec/stdlib/block';
|
|
11
|
+
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
12
12
|
import type {
|
|
13
13
|
IFullNodeBlockBuilder,
|
|
14
14
|
ITxProvider,
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
SlasherConfig,
|
|
17
17
|
} from '@aztec/stdlib/interfaces/server';
|
|
18
18
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
19
|
-
import { OffenseType } from '@aztec/stdlib/slashing';
|
|
19
|
+
import { OffenseType, getOffenseTypeName } from '@aztec/stdlib/slashing';
|
|
20
20
|
import {
|
|
21
21
|
ReExFailedTxsError,
|
|
22
22
|
ReExStateMismatchError,
|
|
@@ -78,49 +78,44 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
78
78
|
|
|
79
79
|
private handlePruneL2Blocks(event: L2BlockPruneEvent): void {
|
|
80
80
|
const { blocks, epochNumber } = event;
|
|
81
|
-
this.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
})
|
|
120
|
-
.catch(error => {
|
|
121
|
-
// This can happen if we fail to get the validators for the epoch.
|
|
122
|
-
this.log.error('Error while creating slash for epoch', error);
|
|
123
|
-
});
|
|
81
|
+
void this.processPruneL2Blocks(blocks, epochNumber).catch(err =>
|
|
82
|
+
this.log.error('Error processing pruned L2 blocks', err, { epochNumber }),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async emitSlashForEpoch(offense: OffenseType, epochNumber: bigint): Promise<void> {
|
|
87
|
+
const validators = await this.getValidatorsForEpoch(epochNumber);
|
|
88
|
+
if (validators.length === 0) {
|
|
89
|
+
this.log.warn(`No validators found for epoch ${epochNumber} (cannot slash for ${getOffenseTypeName(offense)})`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const args = this.validatorsToSlashingArgs(validators, offense, BigInt(epochNumber));
|
|
93
|
+
this.log.verbose(`Created slash for ${getOffenseTypeName(offense)} at epoch ${epochNumber}`, args);
|
|
94
|
+
this.emit(WANT_TO_SLASH_EVENT, args);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private async processPruneL2Blocks(blocks: L2Block[], epochNumber: bigint): Promise<void> {
|
|
98
|
+
try {
|
|
99
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
100
|
+
const epochBlocks = blocks.filter(b => getEpochAtSlot(b.slot, l1Constants) === epochNumber);
|
|
101
|
+
this.log.info(
|
|
102
|
+
`Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`,
|
|
103
|
+
{ blocks: epochBlocks.map(b => b.toBlockInfo()) },
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
await this.validateBlocks(epochBlocks);
|
|
107
|
+
this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
|
|
108
|
+
await this.emitSlashForEpoch(OffenseType.VALID_EPOCH_PRUNED, epochNumber);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (error instanceof TransactionsNotAvailableError) {
|
|
111
|
+
this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, {
|
|
112
|
+
message: error.message,
|
|
113
|
+
});
|
|
114
|
+
await this.emitSlashForEpoch(OffenseType.DATA_WITHHOLDING, epochNumber);
|
|
115
|
+
} else {
|
|
116
|
+
this.log.error(`Error while validating pruned epoch ${epochNumber}. Will not want to slash.`, error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
124
119
|
}
|
|
125
120
|
|
|
126
121
|
public async validateBlocks(blocks: L2Block[]): Promise<void> {
|
|
@@ -151,7 +146,7 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
151
146
|
|
|
152
147
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockFromL1.number);
|
|
153
148
|
const { block, failedTxs, numTxs } = await this.blockBuilder.buildBlock(
|
|
154
|
-
txs
|
|
149
|
+
txs,
|
|
155
150
|
l1ToL2Messages,
|
|
156
151
|
blockFromL1.header.globalVariables,
|
|
157
152
|
{},
|