Skip to content

Commit 61a23c9

Browse files
authored
test(e2e): enable pipelining on p2p tests (#23070)
Toggle pipelining on all e2e p2p tests
1 parent 7e6f438 commit 61a23c9

17 files changed

Lines changed: 101 additions & 14 deletions

yarn-project/end-to-end/src/e2e_p2p/broadcasted_invalid_block_proposal_slash.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ describe('e2e_p2p_broadcasted_invalid_block_proposal_slash', () => {
6262
ethereumSlotDuration: ETHEREUM_SLOT_DURATION,
6363
aztecSlotDuration: AZTEC_SLOT_DURATION,
6464
aztecTargetCommitteeSize: COMMITTEE_SIZE,
65+
enableProposerPipelining: true,
66+
inboxLag: 2,
6567
aztecProofSubmissionEpochs: 1024, // effectively do not reorg
6668
slashInactivityConsecutiveEpochThreshold: 32, // effectively do not slash for inactivity
6769
minTxsPerBlock: 0, // always be building

yarn-project/end-to-end/src/e2e_p2p/data_withholding_slash.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ describe('e2e_p2p_data_withholding_slash', () => {
7373
slashAmountLarge: slashingUnit * 3n,
7474
slashSelfAllowed: true,
7575
minTxsPerBlock: 0,
76+
enableProposerPipelining: true,
77+
inboxLag: 2,
7678
},
7779
});
7880

@@ -165,6 +167,11 @@ describe('e2e_p2p_data_withholding_slash', () => {
165167

166168
// Re-create the nodes.
167169
// ASSUMING they sync in the middle of the epoch, they will "see" the reorg, and try to slash.
170+
// Reset minTxsPerBlock to 0 so re-created validators build empty checkpoints. Under proposer
171+
// pipelining, the vote-offenses signature is bound to the target slot and the multicall is only
172+
// delayed to the target slot start when a checkpoint is being proposed; without a proposal,
173+
// votes would mine in the current wall-clock slot, causing the EIP-712 signature verification to fail.
174+
t.ctx.aztecNodeConfig.minTxsPerBlock = 0;
168175
t.logger.warn('Re-creating nodes');
169176
nodes = await createNodes(
170177
t.ctx.aztecNodeConfig,

yarn-project/end-to-end/src/e2e_p2p/duplicate_attestation_slash.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ describe('e2e_p2p_duplicate_attestation_slash', () => {
8989
slashDuplicateProposalPenalty: slashingUnit,
9090
slashDuplicateAttestationPenalty: slashingUnit,
9191
slashingOffsetInRounds: 1,
92+
enableProposerPipelining: true,
93+
inboxLag: 2,
9294
},
9395
});
9496

yarn-project/end-to-end/src/e2e_p2p/duplicate_proposal_slash.test.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { AztecNodeService } from '@aztec/aztec-node';
22
import type { TestAztecNodeService } from '@aztec/aztec-node/test';
33
import { EthAddress } from '@aztec/aztec.js/addresses';
44
import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
5+
import { retryUntil } from '@aztec/foundation/retry';
56
import { bufferToHex } from '@aztec/foundation/string';
67
import { OffenseType } from '@aztec/slasher';
78
import { TopicType } from '@aztec/stdlib/p2p';
@@ -79,6 +80,8 @@ describe('e2e_p2p_duplicate_proposal_slash', () => {
7980
blockDurationMs: BLOCK_DURATION * 1000,
8081
slashDuplicateProposalPenalty: slashingUnit,
8182
slashingOffsetInRounds: 1,
83+
enableProposerPipelining: true,
84+
inboxLag: 2,
8285
},
8386
});
8487

@@ -220,28 +223,47 @@ describe('e2e_p2p_duplicate_proposal_slash', () => {
220223
t.logger.warn('Starting all sequencers');
221224
await Promise.all(nodes.map(n => n.getSequencer()!.start()));
222225

223-
// Now warp to the target epoch — sequencers are already running
224-
t.logger.warn(`Advancing to target epoch ${targetEpoch}`);
225-
await t.ctx.cheatCodes.rollup.advanceToEpoch(targetEpoch);
226+
// Now warp to one slot before the target epoch — sequencers are already running.
227+
// Under proposer pipelining, the malicious proposers begin building for the first
228+
// slot of the target epoch one slot earlier; warping to the start of the epoch
229+
// would force both AVM-heavy duplicate proposals to serialize past the slot
230+
// boundary, after which honest receivers reject them as late.
231+
t.logger.warn(`Advancing to one slot before target epoch ${targetEpoch}`);
232+
await t.ctx.cheatCodes.rollup.advanceToEpoch(targetEpoch, { offset: -AZTEC_SLOT_DURATION });
226233

227-
// Wait for offense to be detected
228-
// The honest nodes should detect the duplicate proposal from the malicious validator
234+
// Wait for offense to be detected. Under proposer pipelining, checkpoint proposals are broadcast
235+
// at the slot boundary while the receivers' wall clocks may have already advanced past the build
236+
// slot — when that happens, honest nodes reject the gossip with "invalid slot number" before
237+
// duplicate detection runs, so DUPLICATE_PROPOSAL is only observed by whichever node managed to
238+
// process both proposals while still in the build slot (often the other malicious node, since
239+
// they receive each other's broadcasts immediately). We therefore collect offenses from every
240+
// node in the network and assert that at least one of them recorded the duplicate proposal.
229241
t.logger.warn('Waiting for duplicate proposal offense to be detected...');
230-
const offenses = await awaitOffenseDetected({
242+
await awaitOffenseDetected({
231243
epochDuration: t.ctx.aztecNodeConfig.aztecEpochDuration,
232244
logger: t.logger,
233-
nodeAdmin: honestNode1, // Use honest node to check for offenses
245+
nodeAdmin: honestNode1,
234246
slashingRoundSize,
235247
waitUntilOffenseCount: 1,
236248
timeoutSeconds: AZTEC_SLOT_DURATION * 16,
237249
});
238250

239-
t.logger.warn(`Collected offenses`, { offenses });
251+
// Poll every node for DUPLICATE_PROPOSAL offenses, retrying briefly so any node that detected
252+
// the duplicate after the initial offense was collected has time to flush it through the
253+
// slasher's offenses-collector.
254+
const proposalOffenses = await retryUntil(
255+
async () => {
256+
const allOffenses = (await Promise.all(nodes.map(n => n.getSlashOffenses('all')))).flat();
257+
const filtered = allOffenses.filter(o => o.offenseType === OffenseType.DUPLICATE_PROPOSAL);
258+
if (filtered.length > 0) {
259+
return filtered;
260+
}
261+
},
262+
'duplicate proposal offense',
263+
AZTEC_SLOT_DURATION * 4,
264+
);
240265

241-
// Filter to only DUPLICATE_PROPOSAL offenses. The two malicious nodes sharing the same key
242-
// will also each self-attest to their own (different) checkpoint proposals, which causes honest
243-
// nodes to detect a DUPLICATE_ATTESTATION as well. We only care about proposals here.
244-
const proposalOffenses = offenses.filter(o => o.offenseType === OffenseType.DUPLICATE_PROPOSAL);
266+
t.logger.warn(`Collected duplicate proposal offenses`, { proposalOffenses });
245267
expect(proposalOffenses.length).toBeGreaterThan(0);
246268
for (const offense of proposalOffenses) {
247269
expect(offense.validator.toString()).toEqual(maliciousValidatorAddress.toString());

yarn-project/end-to-end/src/e2e_p2p/gossip_network.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ describe('e2e_p2p_network', () => {
6969
slashingRoundSizeInEpochs: 2,
7070
slashingQuorum: 5,
7171
listenAddress: '127.0.0.1',
72+
enableProposerPipelining: true,
73+
inboxLag: 2,
7274
},
7375
});
7476

yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ describe('e2e_p2p_network', () => {
6868
// Without this, no blocks are built until txs arrive, and a failed checkpoint during tx
6969
// submission causes block pruning that invalidates tx references.
7070
minTxsPerBlock: 0,
71+
enableProposerPipelining: true,
72+
inboxLag: 2,
7173
},
7274
});
7375

yarn-project/end-to-end/src/e2e_p2p/inactivity_slash_test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export class P2PInactivityTest {
5858
basePort: BOOT_NODE_UDP_PORT,
5959
startProverNode: true,
6060
initialConfig: {
61+
enableProposerPipelining: true,
62+
inboxLag: 2,
6163
anvilSlotsInAnEpoch: 4,
6264
proverNodeConfig: { proverNodeEpochProvingDelayMs: AZTEC_SLOT_DURATION * 1000 },
6365
aztecTargetCommitteeSize: COMMITTEE_SIZE,

yarn-project/end-to-end/src/e2e_p2p/inactivity_slash_with_consecutive_epochs.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('e2e_p2p_inactivity_slash_with_consecutive_epochs', () => {
5555
return offenses.length > 0 ? offenses : undefined;
5656
},
5757
'slash offenses',
58-
slashInactivityConsecutiveEpochThreshold * aztecEpochDuration * aztecSlotDuration * 2,
58+
slashInactivityConsecutiveEpochThreshold * aztecEpochDuration * aztecSlotDuration * 4,
5959
);
6060
expect(unique(offenses.map(o => o.validator.toString()))).toEqual([offlineValidator.toString()]);
6161
expect(unique(offenses.map(o => o.offenseType))).toEqual([OffenseType.INACTIVITY]);

yarn-project/end-to-end/src/e2e_p2p/multiple_validators_sentinel.parallel.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ describe('e2e_p2p_multiple_validators_sentinel', () => {
5858
slashingRoundSizeInEpochs: 2,
5959
sentinelEnabled: true,
6060
slashInactivityPenalty: 0n, // Set to 0 to disable
61+
enableProposerPipelining: true,
62+
inboxLag: 2,
6163
},
6264
});
6365

@@ -103,7 +105,20 @@ describe('e2e_p2p_multiple_validators_sentinel', () => {
103105
});
104106

105107
it('collects attestations for all validators on a node', async () => {
108+
// Ensure all nodes see each other, especially the sentinel, before starting slot counting
109+
await t.waitForP2PMeshConnectivity([...nodes, sentinel]);
110+
111+
// Wait until validator nodes have advanced past their first proposed slot so that the
112+
// pipelining warm-up period (where some attestations may be missed) is behind us.
106113
await t.monitor.run();
114+
const warmupSlot = Number(t.monitor.l2SlotNumber) + 1;
115+
t.logger.info(`Waiting for warmup slot ${warmupSlot} before establishing initial slot`);
116+
await retryUntil(
117+
async () => (await t.monitor.run()).l2SlotNumber >= warmupSlot,
118+
'warmup slot',
119+
AZTEC_SLOT_DURATION * 3,
120+
);
121+
107122
const { checkpointNumber: initialBlock, l2SlotNumber: initialSlot } = t.monitor;
108123

109124
const timeout = AZTEC_SLOT_DURATION * SLOT_COUNT * 4;

yarn-project/end-to-end/src/e2e_p2p/preferred_gossip_network.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ describe('e2e_p2p_preferred_network', () => {
142142
p2pDisableStatusHandshake: false,
143143
// Just for testing be aggressive here, don't allow any auth handshake failures
144144
p2pMaxFailedAuthAttemptsAllowed: 0,
145+
minTxsPerBlock: 0,
146+
enableProposerPipelining: true,
147+
inboxLag: 2,
145148
},
146149
});
147150

0 commit comments

Comments
 (0)