From 8cf4bbda1705db0e9edfdea8a96592b56734289a Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sun, 19 Jan 2025 17:37:53 +0000 Subject: [PATCH] feat(reqresp): request l2 blocks --- yarn-project/p2p/src/mocks/index.ts | 2 ++ .../p2p/src/services/libp2p/libp2p_service.ts | 6 ++-- .../p2p/src/services/reqresp/interface.ts | 11 ++++++- .../src/services/reqresp/protocols/block.ts | 15 ++++++++++ .../src/services/reqresp/protocols/index.ts | 1 + .../reqresp/rate-limiter/rate_limits.ts | 10 +++++++ .../p2p/src/services/reqresp/reqresp.test.ts | 30 ++++++++++++++++++- 7 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 yarn-project/p2p/src/services/reqresp/protocols/block.ts diff --git a/yarn-project/p2p/src/mocks/index.ts b/yarn-project/p2p/src/mocks/index.ts index 7788db5e73f..179288c4f83 100644 --- a/yarn-project/p2p/src/mocks/index.ts +++ b/yarn-project/p2p/src/mocks/index.ts @@ -153,6 +153,7 @@ export const MOCK_SUB_PROTOCOL_HANDLERS: ReqRespSubProtocolHandlers = { [ReqRespSubProtocol.STATUS]: statusHandler, [ReqRespSubProtocol.TX]: (_msg: any) => Promise.resolve(Buffer.from('tx')), [ReqRespSubProtocol.GOODBYE]: (_msg: any) => Promise.resolve(Buffer.from('goodbye')), + [ReqRespSubProtocol.BLOCK]: (_msg: any) => Promise.resolve(Buffer.from('block')), }; // By default, all requests are valid @@ -162,6 +163,7 @@ export const MOCK_SUB_PROTOCOL_VALIDATORS: ReqRespSubProtocolValidators = { [ReqRespSubProtocol.STATUS]: noopValidator, [ReqRespSubProtocol.TX]: noopValidator, [ReqRespSubProtocol.GOODBYE]: noopValidator, + [ReqRespSubProtocol.BLOCK]: noopValidator, }; /** diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index e92d8ae1620..8c0a1b24b09 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -61,8 +61,7 @@ import { PeerManager } from '../peer-manager/peer_manager.js'; import { PeerScoring } from '../peer-manager/peer_scoring.js'; import { DEFAULT_SUB_PROTOCOL_VALIDATORS, ReqRespSubProtocol, type SubProtocolMap } from '../reqresp/interface.js'; import { reqGoodbyeHandler } from '../reqresp/protocols/goodbye.js'; -import { pingHandler, statusHandler } from '../reqresp/protocols/index.js'; -import { reqRespTxHandler } from '../reqresp/protocols/tx.js'; +import { pingHandler, reqRespBlockHandler, reqRespTxHandler, statusHandler } from '../reqresp/protocols/index.js'; import { ReqResp } from '../reqresp/reqresp.js'; import type { P2PService, PeerDiscoveryService } from '../service.js'; import { GossipSubEvent } from '../types.js'; @@ -300,12 +299,14 @@ export class LibP2PService extends WithTracer implement // Create request response protocol handlers const txHandler = reqRespTxHandler(this.mempools); const goodbyeHandler = reqGoodbyeHandler(this.peerManager); + const blockHandler = reqRespBlockHandler(this.l2BlockSource); const requestResponseHandlers = { [ReqRespSubProtocol.PING]: pingHandler, [ReqRespSubProtocol.STATUS]: statusHandler, [ReqRespSubProtocol.TX]: txHandler.bind(this), [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this), + [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this), }; // Add p2p topic validators @@ -335,6 +336,7 @@ export class LibP2PService extends WithTracer implement // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function const reqrespSubProtocolValidators = { ...DEFAULT_SUB_PROTOCOL_VALIDATORS, + // TODO(#11336): A request validator for blocks [ReqRespSubProtocol.TX]: this.validateRequestedTx.bind(this), }; await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators); diff --git a/yarn-project/p2p/src/services/reqresp/interface.ts b/yarn-project/p2p/src/services/reqresp/interface.ts index 43e5b9a0ecd..820714c60b4 100644 --- a/yarn-project/p2p/src/services/reqresp/interface.ts +++ b/yarn-project/p2p/src/services/reqresp/interface.ts @@ -1,4 +1,5 @@ -import { Tx, TxHash } from '@aztec/circuit-types'; +import { L2Block, Tx, TxHash } from '@aztec/circuit-types'; +import { Fr } from '@aztec/foundation/fields'; import { type PeerId } from '@libp2p/interface'; @@ -9,12 +10,14 @@ export const PING_PROTOCOL = '/aztec/req/ping/0.1.0'; export const STATUS_PROTOCOL = '/aztec/req/status/0.1.0'; export const GOODBYE_PROTOCOL = '/aztec/req/goodbye/0.1.0'; export const TX_REQ_PROTOCOL = '/aztec/req/tx/0.1.0'; +export const BLOCK_REQ_PROTOCOL = '/aztec/req/block/0.1.0'; export enum ReqRespSubProtocol { PING = PING_PROTOCOL, STATUS = STATUS_PROTOCOL, GOODBYE = GOODBYE_PROTOCOL, TX = TX_REQ_PROTOCOL, + BLOCK = BLOCK_REQ_PROTOCOL, } /** @@ -75,6 +78,7 @@ export const DEFAULT_SUB_PROTOCOL_VALIDATORS: ReqRespSubProtocolValidators = { [ReqRespSubProtocol.STATUS]: noopValidator, [ReqRespSubProtocol.TX]: noopValidator, [ReqRespSubProtocol.GOODBYE]: noopValidator, + [ReqRespSubProtocol.BLOCK]: noopValidator, }; /** @@ -101,6 +105,7 @@ export const DEFAULT_SUB_PROTOCOL_HANDLERS: ReqRespSubProtocolHandlers = { [ReqRespSubProtocol.STATUS]: defaultHandler, [ReqRespSubProtocol.TX]: defaultHandler, [ReqRespSubProtocol.GOODBYE]: defaultHandler, + [ReqRespSubProtocol.BLOCK]: defaultHandler, }; /** @@ -158,4 +163,8 @@ export const subProtocolMap: SubProtocolMap = { request: RequestableBuffer, response: RequestableBuffer, }, + [ReqRespSubProtocol.BLOCK]: { + request: Fr, // block number + response: L2Block, + }, }; diff --git a/yarn-project/p2p/src/services/reqresp/protocols/block.ts b/yarn-project/p2p/src/services/reqresp/protocols/block.ts new file mode 100644 index 00000000000..b045f84e53e --- /dev/null +++ b/yarn-project/p2p/src/services/reqresp/protocols/block.ts @@ -0,0 +1,15 @@ +import { type L2BlockSource } from '@aztec/circuit-types'; +import { Fr } from '@aztec/foundation/fields'; + +import { type PeerId } from '@libp2p/interface'; + +import { type ReqRespSubProtocolHandler } from '../interface.js'; + +export function reqRespBlockHandler(l2BlockSource: L2BlockSource): ReqRespSubProtocolHandler { + return async (_peerId: PeerId, msg: Buffer) => { + const blockNumber = Fr.fromBuffer(msg); + + const foundBlock = await l2BlockSource.getBlock(Number(blockNumber)); + return foundBlock ? foundBlock.toBuffer() : Buffer.alloc(0); + }; +} diff --git a/yarn-project/p2p/src/services/reqresp/protocols/index.ts b/yarn-project/p2p/src/services/reqresp/protocols/index.ts index ffc009fe37a..583cadcc447 100644 --- a/yarn-project/p2p/src/services/reqresp/protocols/index.ts +++ b/yarn-project/p2p/src/services/reqresp/protocols/index.ts @@ -5,3 +5,4 @@ export * from './ping.js'; export * from './status.js'; export * from './tx.js'; export * from './goodbye.js'; +export * from './block.js'; diff --git a/yarn-project/p2p/src/services/reqresp/rate-limiter/rate_limits.ts b/yarn-project/p2p/src/services/reqresp/rate-limiter/rate_limits.ts index e0c2dbdb6e4..dde34d64c1e 100644 --- a/yarn-project/p2p/src/services/reqresp/rate-limiter/rate_limits.ts +++ b/yarn-project/p2p/src/services/reqresp/rate-limiter/rate_limits.ts @@ -32,6 +32,16 @@ export const DEFAULT_RATE_LIMITS: ReqRespSubProtocolRateLimits = { quotaCount: 10, }, }, + [ReqRespSubProtocol.BLOCK]: { + peerLimit: { + quotaTimeMs: 1000, + quotaCount: 2, + }, + globalLimit: { + quotaTimeMs: 1000, + quotaCount: 5, + }, + }, [ReqRespSubProtocol.GOODBYE]: { peerLimit: { quotaTimeMs: 1000, diff --git a/yarn-project/p2p/src/services/reqresp/reqresp.test.ts b/yarn-project/p2p/src/services/reqresp/reqresp.test.ts index 2fbdfce7456..35b6fac7a8f 100644 --- a/yarn-project/p2p/src/services/reqresp/reqresp.test.ts +++ b/yarn-project/p2p/src/services/reqresp/reqresp.test.ts @@ -1,4 +1,5 @@ -import { PeerErrorSeverity, TxHash, mockTx } from '@aztec/circuit-types'; +import { L2Block, type L2BlockSource, PeerErrorSeverity, TxHash, mockTx } from '@aztec/circuit-types'; +import { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; @@ -19,6 +20,7 @@ import { import { type PeerManager } from '../peer-manager/peer_manager.js'; import { type PeerScoring } from '../peer-manager/peer_scoring.js'; import { ReqRespSubProtocol, RequestableBuffer } from './interface.js'; +import { reqRespBlockHandler } from './protocols/block.js'; import { GoodByeReason, reqGoodbyeHandler } from './protocols/goodbye.js'; const PING_REQUEST = RequestableBuffer.fromBuffer(Buffer.from('ping')); @@ -341,6 +343,32 @@ describe('ReqResp', () => { }); }); + describe('Block protocol', () => { + it('should handle block requests', async () => { + const blockNumber = 1; + const blockNumberFr = Fr.ONE; + const block = L2Block.random(blockNumber); + + const l2BlockSource: MockProxy = mock(); + l2BlockSource.getBlock.mockImplementation((_blockNumber: number) => { + return Promise.resolve(block); + }); + + const protocolHandlers = MOCK_SUB_PROTOCOL_HANDLERS; + protocolHandlers[ReqRespSubProtocol.BLOCK] = reqRespBlockHandler(l2BlockSource); + + nodes = await createNodes(peerScoring, 2); + + await startNodes(nodes, protocolHandlers); + await sleep(500); + await connectToPeers(nodes); + await sleep(500); + + const res = await nodes[0].req.sendRequest(ReqRespSubProtocol.BLOCK, blockNumberFr); + expect(res).toEqual(block); + }); + }); + describe('Batch requests', () => { it('should send a batch request between many peers', async () => { const batchSize = 9;