P2P Networking
Building peer-to-peer networks for blockchain nodes.
Advanced⏱️ 55 min📚 Prerequisites: 1
P2P Networking
Blockchain nodes communicate via peer-to-peer (P2P) networks. Each node connects to multiple peers to share blocks and transactions.
P2P Network Structure
- No central server: All nodes are equal
- Mesh topology: Nodes connect to multiple peers
- Discovery: Nodes find each other via bootstrapping
- Gossip protocol: Information spreads through network
Node Structure
RUSTstruct Node { id: String, address: String, peers: Vec<Peer>, blockchain: Blockchain, } struct Peer { id: String, address: String, is_connected: bool, }
Connection Management
RUSTimpl Node { fn connect_to_peer(&mut self, peer_address: String) -> Result<(), String> { // Establish connection to peer // Add to peers list Ok(()) } fn disconnect_from_peer(&mut self, peer_id: String) { // Remove peer from list } fn broadcast_block(&self, block: &Block) { // Send block to all connected peers } }
Message Types
- Handshake: Initial connection
- Block: New block announcement
- Transaction: New transaction
- GetBlocks: Request blocks
- Blocks: Send requested blocks
- Ping/Pong: Keep-alive
Network Discovery
- Bootstrap nodes: Known entry points
- Peer exchange: Peers share their peer lists
- DNS seeds: DNS records with node addresses
- DHT: Distributed hash table for discovery
Advantages
- Resilient: No single point of failure
- Scalable: More nodes = more capacity
- Censorship resistant: Hard to shut down
- Decentralized: No central authority
Code Examples
Node Structure
Basic P2P node structure
RUST
struct Peer {
id: String,
address: String,
is_connected: bool,
}
struct Node {
id: String,
address: String,
peers: Vec<Peer>,
}
impl Node {
fn new(id: String, address: String) -> Self {
Node {
id,
address,
peers: Vec::new(),
}
}
fn add_peer(&mut self, peer: Peer) {
self.peers.push(peer);
println!("Added peer: {} at {}", peer.id, peer.address);
}
fn get_peer_count(&self) -> usize {
self.peers.len()
}
fn broadcast(&self, message: &str) {
println!("Broadcasting '{}' to {} peers", message, self.peers.len());
for peer in &self.peers {
if peer.is_connected {
println!(" -> Sending to peer: {}", peer.id);
}
}
}
}
fn main() {
let mut node = Node::new(
String::from("node1"),
String::from("127.0.0.1:8080"),
);
node.add_peer(Peer {
id: String::from("peer1"),
address: String::from("127.0.0.1:8081"),
is_connected: true,
});
node.add_peer(Peer {
id: String::from("peer2"),
address: String::from("127.0.0.1:8082"),
is_connected: true,
});
println!("Node has {} peers", node.get_peer_count());
node.broadcast("New block");
}Explanation:
A P2P node maintains connections to multiple peers. It can broadcast messages to all connected peers, enabling information propagation through the network.
Message Types
Different message types in P2P network
RUST
enum Message {
Handshake { node_id: String, version: u32 },
Block { block_data: String },
Transaction { tx_data: String },
GetBlocks { from_height: u64 },
Blocks { blocks: Vec<String> },
Ping,
Pong,
}
fn handle_message(msg: Message) {
match msg {
Message::Handshake { node_id, version } => {
println!("Handshake from {} version {}", node_id, version);
},
Message::Block { block_data } => {
println!("Received block: {}", block_data);
},
Message::Transaction { tx_data } => {
println!("Received transaction: {}", tx_data);
},
Message::GetBlocks { from_height } => {
println!("Requesting blocks from height {}", from_height);
},
Message::Blocks { blocks } => {
println!("Received {} blocks", blocks.len());
},
Message::Ping => {
println!("Received ping, sending pong");
},
Message::Pong => {
println!("Received pong");
},
}
}
fn main() {
handle_message(Message::Handshake {
node_id: String::from("node1"),
version: 1,
});
handle_message(Message::Block {
block_data: String::from("block123"),
});
}Explanation:
P2P networks use different message types for various operations. Enums in Rust are perfect for representing these message types.
Exercises
Create P2P Node
Create a node that can add peers and broadcast messages!
Starter Code:
RUST
struct Peer {
id: String,
address: String,
}
struct Node {
id: String,
peers: Vec<Peer>,
}
fn main() {
let mut node = Node::new(String::from("node1"));
node.add_peer(Peer {
id: String::from("peer1"),
address: String::from("127.0.0.1:8081"),
});
node.broadcast("Hello");
}