diff --git a/yarn-project/p2p/src/client/p2p_client.test.ts b/yarn-project/p2p/src/client/p2p_client.test.ts index b4fbf816d64e..a205e677c3a9 100644 --- a/yarn-project/p2p/src/client/p2p_client.test.ts +++ b/yarn-project/p2p/src/client/p2p_client.test.ts @@ -1,4 +1,5 @@ import { MockL2BlockSource } from '@aztec/archiver/test'; +import { Signature } from '@aztec/foundation/eth-signature'; import { Fr } from '@aztec/foundation/fields'; import { retryUntil } from '@aztec/foundation/retry'; import { sleep } from '@aztec/foundation/sleep'; @@ -8,10 +9,10 @@ import { L2Block, randomPublishedL2Block } from '@aztec/stdlib/block'; import { P2PClientType } from '@aztec/stdlib/p2p'; import { mockTx } from '@aztec/stdlib/testing'; -import { expect } from '@jest/globals'; +import { expect, jest } from '@jest/globals'; import { type MockProxy, mock } from 'jest-mock-extended'; -import type { P2PService } from '../index.js'; +import { InMemoryAttestationPool, type P2PService } from '../index.js'; import type { AttestationPool } from '../mem_pools/attestation_pool/attestation_pool.js'; import type { MemPools } from '../mem_pools/interface.js'; import type { TxPool } from '../mem_pools/tx_pool/index.js'; @@ -19,7 +20,7 @@ import { P2PClient } from './p2p_client.js'; describe('In-Memory P2P Client', () => { let txPool: MockProxy; - let attestationPool: MockProxy; + let attestationPool: AttestationPool; let mempools: MemPools; let blockSource: MockL2BlockSource; let p2pService: MockProxy; @@ -35,7 +36,7 @@ describe('In-Memory P2P Client', () => { p2pService = mock(); - attestationPool = mock(); + attestationPool = new InMemoryAttestationPool(); blockSource = new MockL2BlockSource(); await blockSource.createBlocks(100); @@ -236,15 +237,17 @@ describe('In-Memory P2P Client', () => { const advanceToProvenBlockNumber = 20; const keepAttestationsInPoolFor = 12; + const deleteAttestationsOlderThanSpy = jest.spyOn(attestationPool, 'deleteAttestationsOlderThan'); + blockSource.setProvenBlockNumber(0); (client as any).keepAttestationsInPoolFor = keepAttestationsInPoolFor; await client.start(); - expect(attestationPool.deleteAttestationsOlderThan).not.toHaveBeenCalled(); + expect(deleteAttestationsOlderThanSpy).not.toHaveBeenCalled(); await advanceToProvenBlock(advanceToProvenBlockNumber); - expect(attestationPool.deleteAttestationsOlderThan).toHaveBeenCalledTimes(1); - expect(attestationPool.deleteAttestationsOlderThan).toHaveBeenCalledWith( + expect(deleteAttestationsOlderThanSpy).toHaveBeenCalledTimes(1); + expect(deleteAttestationsOlderThanSpy).toHaveBeenCalledWith( BigInt(advanceToProvenBlockNumber - keepAttestationsInPoolFor), ); }); @@ -254,10 +257,22 @@ describe('In-Memory P2P Client', () => { it('adds attestations to the pool', async () => { await client.start(); const block = await randomPublishedL2Block(1); + const addAttestationsSpy = jest.spyOn(attestationPool, 'addAttestations'); await client.handleBlockStreamEvent({ type: 'blocks-added', blocks: [block] }); - expect(attestationPool.addAttestations).toHaveBeenCalledWith( + expect(addAttestationsSpy).toHaveBeenCalledWith( block.signatures.map(signature => expect.objectContaining({ signature })), ); }); + + it('handles empty signatures in block stream events', async () => { + await client.start(); + const block = await randomPublishedL2Block(1); + block.signatures[0] = Signature.empty(); + const addAttestationsSpy = jest.spyOn(attestationPool, 'addAttestations'); + await client.handleBlockStreamEvent({ type: 'blocks-added', blocks: [block] }); + expect(addAttestationsSpy).toHaveBeenCalledWith( + block.signatures.filter(sig => !sig.isEmpty).map(signature => expect.objectContaining({ signature })), + ); + }); }); }); diff --git a/yarn-project/p2p/src/client/p2p_client.ts b/yarn-project/p2p/src/client/p2p_client.ts index bfd1c5d41e88..0027d15dbee6 100644 --- a/yarn-project/p2p/src/client/p2p_client.ts +++ b/yarn-project/p2p/src/client/p2p_client.ts @@ -627,7 +627,7 @@ export class P2PClient private async addAttestationsToPool(blocks: PublishedL2Block[]): Promise { const attestations = blocks.flatMap(block => { const payload = ConsensusPayload.fromBlock(block.block); - return block.signatures.map(signature => new BlockAttestation(payload, signature)); + return block.signatures.filter(sig => !sig.isEmpty).map(signature => new BlockAttestation(payload, signature)); }); await this.attestationPool?.addAttestations(attestations); const slots = blocks.map(b => b.block.header.getSlot()).sort((a, b) => Number(a - b)); diff --git a/yarn-project/stdlib/src/block/published_l2_block.ts b/yarn-project/stdlib/src/block/published_l2_block.ts index ca4df4792559..c88e58a1a7d1 100644 --- a/yarn-project/stdlib/src/block/published_l2_block.ts +++ b/yarn-project/stdlib/src/block/published_l2_block.ts @@ -1,5 +1,6 @@ import { Buffer32 } from '@aztec/foundation/buffer'; import { times } from '@aztec/foundation/collection'; +import { Secp256k1Signer } from '@aztec/foundation/crypto'; import { Signature } from '@aztec/foundation/eth-signature'; import { schemas } from '@aztec/foundation/schemas'; import { L2Block } from '@aztec/stdlib/block'; @@ -35,6 +36,10 @@ export async function randomPublishedL2Block(l2BlockNumber: number): Promise Secp256k1Signer.random()); + const signatures = await Promise.all( + times(3, async i => signers[i].signMessage(Buffer32.fromField(await block.hash()))), + ); return { block, l1, signatures }; }