The P2P service in fuel-core implements a three-layer architecture that separates concerns between orchestration logic, service abstraction, and network protocol implementation. The top layer (Task) orchestrates P2P operations and coordinates with other fuel-core services, the middle layer (FuelP2PService) wraps libp2p functionality, and the bottom layer (FuelBehaviour) implements the network protocols.
The architecture uses a port-based design where the Task depends on trait abstractions (TaskP2PService, Broadcast, TxPool, AtomicView) rather than concrete implementations. This enables:
SyncProcessor, AsyncProcessor)SharedState)The Task communicates with other services through:
mpsc::Receiver<TaskRequest> for service requestsSharedState with multiple broadcast::Sender channels for eventsFor details about the underlying libp2p protocols (Gossipsub, RequestResponse, Heartbeat, etc.), see page 5.3. For peer reputation management and the PeerManager, see page 5.4.
The P2P service is organized into three distinct layers, each with specific responsibilities:
Three-Layer P2P Architecture with Port-Based Design
Sources: crates/services/p2p/src/service.rs1-1200 crates/services/p2p/src/p2p_service.rs1-866 crates/services/p2p/src/behavior.rs1-261
| Layer | Component | Responsibility | Location |
|---|---|---|---|
| Task (Layer 1) | Task<P, V, B, T> | Orchestrates P2P operations, processes service requests, manages event loop | crates/services/p2p/src/service.rs447-470 |
SharedState | Provides broadcast channels for inter-service communication | crates/services/p2p/src/service.rs395-430 | |
TaskRequest | Enum of all possible requests from other services | crates/services/p2p/src/service.rs128-189 | |
| Service (Layer 2) | FuelP2PService | Wraps libp2p Swarm, manages peer connections and message encoding | crates/services/p2p/src/p2p_service.rs104-145 |
TaskP2PService | Trait abstraction for P2P operations used by Task | crates/services/p2p/src/service.rs246-288 | |
FuelP2PEvent | Events emitted from P2P layer to Task | crates/services/p2p/src/p2p_service.rs164-188 | |
| Protocol (Layer 3) | FuelBehaviour | Composite libp2p behavior implementing networking protocols | crates/services/p2p/src/behavior.rs47-75 |
Swarm<FuelBehaviour> | libp2p's main networking component | crates/services/p2p/src/p2p_service.rs115 |
Sources: crates/services/p2p/src/service.rs128-470 crates/services/p2p/src/p2p_service.rs104-188 crates/services/p2p/src/behavior.rs47-75
The Task layer is the top-level orchestrator that coordinates between fuel-core services and the P2P network. It implements the RunnableTask trait and manages the main event loop.
The Task<P, V, B, T> struct is the central orchestrator with generic parameters for dependency injection:
Task Component Architecture
Sources: crates/services/p2p/src/service.rs447-470
The Task uses generic parameters for testability and flexibility through the ports and adapters pattern:
| Parameter | Trait Bound | Concrete Type | Purpose |
|---|---|---|---|
P | TaskP2PService | FuelP2PService | Network operations abstraction |
V | AtomicView | View provider | Database access with atomic snapshots |
B | Broadcast | SharedState | Inter-service broadcast channels |
T | TxPool | TxPool port | Transaction pool queries |
The TaskP2PService trait defines the port for P2P operations:
crates/services/p2p/src/service.rs246-288
Key methods:
publish_message(): Broadcast gossipsub messagessend_request_msg(): Send request/response protocol requestssend_response_msg(): Send responses to inbound requestsreport_message(): Report gossipsub message validation resultsreport_peer(): Report peer reputation scoresupdate_block_height(): Update advertised block heightget_peer_id_with_height(): Find peer with specific block heightThe Broadcast trait defines the port for notifying other services:
crates/services/p2p/src/service.rs372-393
Sources: crates/services/p2p/src/service.rs246-288 crates/services/p2p/src/service.rs372-393 crates/services/p2p/src/service.rs447-470
The Task implements RunnableTask::run() with a tokio::select! event loop that handles four event sources with priority ordering:
Task Event Loop Flow with Method Names
Sources: crates/services/p2p/src/service.rs934-1093
The event loop uses biased select to establish priority ordering:
watcher.while_started()next_block_height.next()TaskRequest from other services via request_receiver.recv()p2p_service.next_event()Each event source returns TaskNextAction to control the event loop:
TaskNextAction::Stop: Terminates the serviceTaskNextAction::Continue: Continues polling eventsTaskNextAction::ErrorContinue(err): Logs error and continuescrates/services/p2p/src/service.rs942-1093
Sources: crates/services/p2p/src/service.rs934-1093
CLI Argument Parsing: The P2PArgs struct captures command-line configuration:
Config Initialization: The Config::init() method transforms Config<NotInitialized> into Config<Initialized> by computing the genesis checksum:
crates/services/p2p/src/config.rs169-214
UninitializedTask::new() creates the uninitialized service with dependencies:crates/services/p2p/src/service.rs504-529
into_task() method creates the running Task and underlying FuelP2PService:crates/services/p2p/src/service.rs839-931
Key initialization parameters:
| Configuration | Field | Default | Description |
|---|---|---|---|
| Network Identity | keypair | Required | Secp256k1 keypair for peer ID |
| Max Block Size | max_block_size | 260 MiB | Maximum message size |
| Headers per Request | max_headers_per_request | 100 | Batch size for header sync |
| Txs per Request | max_txs_per_request | 10000 | Batch size for transaction sync |
| DB Read Threads | database_read_threads | 2 | Thread pool for database queries |
| TxPool Threads | tx_pool_threads | 0 | Thread pool for txpool queries |
| Request Timeout | set_request_timeout | 20s | Timeout for req/res protocol |
| Cache Size | cache_size | 1535 | Size of block/tx cache |
Sources: crates/services/p2p/src/config.rs56-160 bin/fuel-core/src/cli/run/p2p.rs39-223
Services send TaskRequest messages to the Task for network operations. The enum defines all possible requests:
TaskRequest Variants and Handling
Sources: crates/services/p2p/src/service.rs128-189 crates/services/p2p/src/service.rs594-814
Broadcast Requests (lines 959-973): Publish messages via gossipsub immediately
Sync Requests (lines 974-1000):
get_peer_id_with_height()send_request_msg() with ResponseSender channelFuelP2PEvent::MessageHeavy Database Requests (lines 615-727):
view_provider.latest_view()db_heavy_task_processor.try_spawn()cached_view.get_sealed_headers() or cached_view.get_transactions()request_sender to avoid blockingHeavy TxPool Requests (lines 729-814):
tx_pool_heavy_task_processor.try_spawn()tx_pool.get_tx_ids() or tx_pool.get_full_txs() asynchronouslySources: crates/services/p2p/src/service.rs594-814 crates/services/p2p/src/service.rs959-1038
The Task periodically checks peer heartbeat health and reports reputation scores:
crates/services/p2p/src/service.rs537-576
Configuration:
heartbeat_check_interval: 5s (how often to check all peers)heartbeat_max_avg_interval: 20s (max acceptable average between heartbeats)heartbeat_max_time_since_last: 40s (max time since last heartbeat)Penalties (hardcoded at lines 874-877):
Sources: crates/services/p2p/src/service.rs537-576 crates/services/p2p/src/service.rs874-877
The Service layer wraps libp2p functionality and provides a clean interface for the Task layer. The FuelP2PService struct encapsulates all libp2p operations.
FuelP2PService Components and Methods
Sources: crates/services/p2p/src/p2p_service.rs104-145 crates/services/p2p/src/p2p_service.rs217-866
The FuelP2PService implements TaskP2PService trait providing these core operations:
crates/services/p2p/src/p2p_service.rs379-396
Steps:
network_metadata.gossipsub_data.topicsgossipsub_codec.encode()swarm.behaviour_mut().publish_message()InsufficientPeers error gracefullycrates/services/p2p/src/p2p_service.rs398-430
Steps:
swarm.behaviour_mut().send_request_msg()ResponseSender into outbound_requests_tableOutboundRequestId for trackingcrates/services/p2p/src/p2p_service.rs652-748
When a response arrives:
outbound_requests_table by OutboundRequestIdResponseSender variant(PeerId, Result<T, ResponseError>) through channelResponseError::TypeMismatchcrates/services/p2p/src/p2p_service.rs433-454
Steps:
ResponseChannel in inbound_requests_tableswarm.behaviour_mut().send_response_msg()The FuelP2PService::next_event() method processes libp2p Swarm events:
crates/services/p2p/src/p2p_service.rs519-547
It filters and transforms SwarmEvent into FuelP2PEvent:
Behaviour(FuelBehaviourEvent) → delegates to handle_behaviour_event()NewListenAddr → logs and returns NoneNoneSources: crates/services/p2p/src/p2p_service.rs217-866
Events emitted from FuelP2PService to the Task:
crates/services/p2p/src/p2p_service.rs164-188
| Event | Data | Purpose |
|---|---|---|
GossipsubMessage | peer_id, message_id, topic_hash, message | Gossipsub message received |
NewSubscription | peer_id, tag | Peer subscribed to topic |
InboundRequestMessage | request_id, request_message | Request/response request received |
PeerConnected | PeerId | New peer connected |
PeerDisconnected | PeerId | Peer disconnected |
PeerInfoUpdated | peer_id, block_height | Peer's block height updated via heartbeat |
Sources: crates/services/p2p/src/p2p_service.rs164-188
The service maintains two tables to track request/response lifecycle:
Request/Response Lifecycle
Sources: crates/services/p2p/src/p2p_service.rs123-129 crates/services/p2p/src/p2p_service.rs398-454 crates/services/p2p/src/p2p_service.rs652-804
The SharedState struct provides broadcast channels for the P2P service to notify other fuel-core services:
SharedState Channel Architecture
Sources: crates/services/p2p/src/service.rs395-430
The SharedState implements the Broadcast trait providing methods for the Task to notify services:
crates/services/p2p/src/service.rs372-430
Methods:
report_peer(): Report peer reputation scoresblock_height_broadcast(): Notify of peer block heightstx_broadcast(): Forward received transactionspre_confirmation_broadcast(): Forward pre-confirmationsnew_tx_subscription_broadcast(): Signal peer subscriptions| Channel | Data Type | Sender | Receiver | Purpose |
|---|---|---|---|---|
tx_broadcast | TransactionGossipData | Task | TxPool | Received transactions for validation |
block_height_broadcast | BlockHeightHeartbeatData | Task | Sync | Peer heights for sync coordination |
pre_confirmations_broadcast | P2PPreConfirmationGossipData | Task | PreConfirmation | Pre-confirmation messages |
new_tx_subscription_broadcast | FuelPeerId | Task | TxPool | Peer subscribed to tx topic |
reserved_peers_broadcast | usize | PeerManager | Monitoring | Reserved peer count updates |
request_sender | TaskRequest | Services | Task | Service requests to P2P |
Sources: crates/services/p2p/src/service.rs395-430
The Protocol layer consists of libp2p's Swarm<FuelBehaviour> and the FuelBehaviour composite. This layer is detailed in page 5.2 (libp2p Behaviors and Protocols), but the key integration points are:
The FuelP2PService wraps the Swarm<FuelBehaviour>:
crates/services/p2p/src/p2p_service.rs115
The service calls into the swarm for all network operations:
FuelBehaviour emits FuelBehaviourEvent which the service transforms into FuelP2PEvent:
crates/services/p2p/src/p2p_service.rs565-589
Event flow:
Swarm polls FuelBehaviourFuelBehaviour emits FuelBehaviourEventFuelP2PService::handle_behaviour_event() processes itFuelP2PEvent to TaskSharedStateSources: crates/services/p2p/src/p2p_service.rs115 crates/services/p2p/src/p2p_service.rs565-589 crates/services/p2p/src/behavior.rs47-75
The P2P service initialization follows the fuel-core service lifecycle with state transitions:
Service Initialization Flow
Sources: bin/fuel-core/src/cli/run/p2p.rs271-368 crates/services/p2p/src/config.rs169-214 crates/services/p2p/src/service.rs504-531 crates/services/p2p/src/service.rs839-931
CLI Configuration (p2p.rs:271-368): P2PArgs::into_config() creates Config<NotInitialized>
Genesis Initialization (config.rs:169-214): Config::init(genesis) computes checksum and transitions to Config<Initialized>
Service Creation (service.rs:504-531): UninitializedTask::new() creates uninitialized service with dependencies
Task Initialization (service.rs:839-931): into_task() performs:
FuelP2PService with initialized configFuelBehaviour and libp2p SwarmSyncProcessor, AsyncProcessor)CachedView)TaskSources: bin/fuel-core/src/cli/run/p2p.rs271-368 crates/services/p2p/src/config.rs169-214 crates/services/p2p/src/service.rs504-531 crates/services/p2p/src/service.rs839-931
To prevent blocking the Task event loop, expensive operations are offloaded to thread pools:
Heavy Operation Offloading Pattern
Sources: crates/services/p2p/src/service.rs615-683 crates/services/p2p/src/service.rs729-814
The Task creates two specialized processors during initialization:
crates/services/p2p/src/service.rs894-900
| Processor | Type | Threads | Queue Size | Purpose |
|---|---|---|---|---|
db_heavy_task_processor | SyncProcessor | database_read_threads (default: 2) | 10,240 | Blocking database reads |
tx_pool_heavy_task_processor | AsyncProcessor | tx_pool_threads (default: 0) | 32 | Async transaction pool queries |
The CachedView wraps an LRU cache to avoid repeated database queries:
crates/services/p2p/src/service.rs922-927
Cache configuration:
cache_size parameterSources: crates/services/p2p/src/service.rs615-814 crates/services/p2p/src/service.rs894-927
P2P configuration uses a two-phase initialization pattern:
crates/services/p2p/src/config.rs56-160 defines Config<State> with two states:
Config<NotInitialized>: Contains all configuration fields but missing genesis checksumConfig<Initialized>: Includes computed checksum for protocol verificationThe transition happens via Config::init(genesis):
crates/services/p2p/src/config.rs169-214
| Field | Type | Default | Description |
|---|---|---|---|
keypair | libp2p::Keypair | Required | Secp256k1 keypair for peer identity |
network_name | String | Required | Network identifier (e.g., "testnet") |
checksum | Checksum | From genesis | SHA256 hash of genesis for protocol version |
max_block_size | NonZeroU32 | 260 MiB | Maximum request/response size |
max_headers_per_request | usize | 100 | Batch size for header sync |
max_txs_per_request | usize | 10000 | Batch size for transaction sync |
bootstrap_nodes | Vec<Multiaddr> | [] | Initial peers for discovery |
reserved_nodes | Vec<Multiaddr> | [] | Always-connected peers |
max_functional_peers_connected | u32 | 50 | Max non-discovery peers |
max_discovery_peers_connected | u32 | 10000 | Max discovery-only peers |
database_read_threads | usize | 2 | Thread pool size for DB reads |
tx_pool_threads | usize | 0 | Async pool size for txpool queries |
set_request_timeout | Duration | 20s | Timeout for req/res protocol |
cache_size | Option<NonZeroUsize> | 1535 | LRU cache size |
Sources: crates/services/p2p/src/config.rs56-214 bin/fuel-core/src/cli/run/p2p.rs39-223
The P2P service architecture implements a clean three-layer design with port-based abstraction:
Layer 1: Task Orchestration (Task<P, V, B, T>)
TaskRequest enum (mpsc channel)SharedState (broadcast channels)tokio::select!SyncProcessor and AsyncProcessorLayer 2: Service Wrapper (FuelP2PService)
Swarm<FuelBehaviour>TaskP2PService trait (port for Task layer)GossipsubMessageHandler<PostcardCodec>PeerManager for reputation scoringFuelP2PEvent to Task layerLayer 3: Protocol Implementation (FuelBehaviour)
SwarmKey Architectural Patterns:
| Pattern | Implementation | Benefit |
|---|---|---|
| Ports and Adapters | TaskP2PService, Broadcast, TxPool, AtomicView traits | Testing, decoupling, dependency injection |
| Offloading | SyncProcessor (DB reads), AsyncProcessor (TxPool queries) | Non-blocking event loop |
| Caching | CachedView with LRU cache (default 1535 entries) | Reduced database load |
| Broadcast Channels | SharedState with multiple broadcast::Sender | Loosely-coupled inter-service communication |
| Request Tracking | outbound_requests_table, inbound_requests_table | Request/response lifecycle management |
| Configuration State Machine | Config<NotInitialized> → Config<Initialized> | Type-safe initialization |
The architecture ensures non-blocking operation, clean separation of concerns, and efficient resource utilization through careful use of async patterns and thread pool offloading.
Sources: crates/services/p2p/src/service.rs1-1200 crates/services/p2p/src/p2p_service.rs1-866 crates/services/p2p/src/behavior.rs1-261
The P2P service provides test helpers for creating networks of nodes:
Bootstrap Node Creation:
Creates a lightweight bootstrap node for testing peer discovery.
crates/fuel-core/src/p2p_test_helpers.rs193-251
Producer/Validator Setup: Configurable node setups with transaction generation and network topology.
crates/fuel-core/src/p2p_test_helpers.rs133-162
Network Creation: Helper to create multiple interconnected nodes with custom configurations.
Refresh this wiki