Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions yarn-project/slasher/src/watchers/epoch_prune_watcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { EpochCache } from '@aztec/epoch-cache';
import { EthAddress } from '@aztec/foundation/eth-address';
import { sleep } from '@aztec/foundation/sleep';
import { L2Block, type L2BlockSourceEventEmitter, L2BlockSourceEvents } from '@aztec/stdlib/block';
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
import type {
BuildBlockResult,
IFullNodeBlockBuilder,
Expand All @@ -28,6 +29,10 @@ describe('EpochPruneWatcher', () => {
let txProvider: MockProxy<Pick<ITxProvider, 'getAvailableTxs'>>;
let blockBuilder: MockProxy<IFullNodeBlockBuilder>;
let fork: MockProxy<MerkleTreeWriteOperations>;

let ts: bigint;
let l1Constants: L1RollupConstants;

const validEpochPrunedPenalty = BigInt(1000000000000000000n);
const dataWithholdingPenalty = BigInt(2000000000000000000n);

Expand All @@ -41,6 +46,18 @@ describe('EpochPruneWatcher', () => {
fork = mock<MerkleTreeWriteOperations>();
blockBuilder.getFork.mockResolvedValue(fork);

ts = BigInt(Math.ceil(Date.now() / 1000));
l1Constants = {
l1StartBlock: 1n,
l1GenesisTime: ts,
slotDuration: 24,
epochDuration: 8,
ethereumSlotDuration: 12,
proofSubmissionEpochs: 1,
};

epochCache.getL1Constants.mockReturnValue(l1Constants);

watcher = new EpochPruneWatcher(l2BlockSource, l1ToL2MessageSource, epochCache, txProvider, blockBuilder, {
slashPrunePenalty: validEpochPrunedPenalty,
slashDataWithholdingPenalty: dataWithholdingPenalty,
Expand All @@ -54,9 +71,10 @@ describe('EpochPruneWatcher', () => {

it('should emit WANT_TO_SLASH_EVENT when a validator is in a pruned epoch when data is unavailable', async () => {
const emitSpy = jest.spyOn(watcher, 'emit');
const epochNumber = 1n;

const block = await L2Block.random(
1, // block number
12, // block number
4, // txs per block
);
txProvider.getAvailableTxs.mockResolvedValue({ txs: [], missingTxs: [block.body.txEffects[0].txHash] });
Expand All @@ -68,11 +86,11 @@ describe('EpochPruneWatcher', () => {
epochCache.getCommitteeForEpoch.mockResolvedValue({
committee: committee.map(EthAddress.fromString),
seed: 0n,
epoch: 1n,
epoch: epochNumber,
});

l2BlockSource.emit(L2BlockSourceEvents.L2PruneDetected, {
epochNumber: 1n,
epochNumber,
blocks: [block],
type: L2BlockSourceEvents.L2PruneDetected,
});
Expand All @@ -85,13 +103,13 @@ describe('EpochPruneWatcher', () => {
validator: EthAddress.fromString(committee[0]),
amount: dataWithholdingPenalty,
offenseType: OffenseType.DATA_WITHHOLDING,
epochOrSlot: 1n,
epochOrSlot: epochNumber,
},
{
validator: EthAddress.fromString(committee[1]),
amount: dataWithholdingPenalty,
offenseType: OffenseType.DATA_WITHHOLDING,
epochOrSlot: 1n,
epochOrSlot: epochNumber,
},
] satisfies WantToSlashArgs[]);
});
Expand All @@ -100,7 +118,7 @@ describe('EpochPruneWatcher', () => {
const emitSpy = jest.spyOn(watcher, 'emit');

const block = await L2Block.random(
1, // block number
12, // block number
4, // txs per block
);
const tx = Tx.random();
Expand Down Expand Up @@ -152,11 +170,11 @@ describe('EpochPruneWatcher', () => {
const emitSpy = jest.spyOn(watcher, 'emit');

const blockFromL1 = await L2Block.random(
1, // block number
12, // block number
1, // txs per block
);
const blockFromBuilder = await L2Block.random(
2, // block number
13, // block number
1, // txs per block
);
const tx = Tx.random();
Expand Down
10 changes: 8 additions & 2 deletions yarn-project/slasher/src/watchers/epoch_prune_watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
type L2BlockSourceEventEmitter,
L2BlockSourceEvents,
} from '@aztec/stdlib/block';
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
import type {
IFullNodeBlockBuilder,
ITxProvider,
Expand Down Expand Up @@ -78,9 +79,14 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter

private handlePruneL2Blocks(event: L2BlockPruneEvent): void {
const { blocks, epochNumber } = event;
this.log.info(`Detected chain prune. Validating epoch ${epochNumber}`);
const l1Constants = this.epochCache.getL1Constants();
const epochBlocks = blocks.filter(b => getEpochAtSlot(b.slot, l1Constants) === epochNumber);
this.log.info(
`Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`,
{ blocks: epochBlocks.map(b => b.toBlockInfo()) },
);

this.validateBlocks(blocks)
this.validateBlocks(epochBlocks)
.then(async () => {
this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
const validators = await this.getValidatorsForEpoch(epochNumber);
Expand Down
Loading