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

RUST
struct Node {
    id: String,
    address: String,
    peers: Vec<Peer>,
    blockchain: Blockchain,
}

struct Peer {
    id: String,
    address: String,
    is_connected: bool,
}

Connection Management

RUST
impl 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

  1. Bootstrap nodes: Known entry points
  2. Peer exchange: Peers share their peer lists
  3. DNS seeds: DNS records with node addresses
  4. 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!

Medium

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");
}