# Rust P2P Plan

We will follow the **Rust** and the **`libp2p`** library, as it is the most flexible and comprehensive choice.

***

### **Prerequisites**

Before we begin, ensure you have the Rust toolchain installed. If not, you can get it from [rust-lang.org](https://www.rust-lang.org/tools/install).

### **Step 1: The First Peer – Identity and Basic Structure**

The design is for every node to have a unique identity. In `libp2p`, this is achieved through a cryptographic keypair, from which a unique `PeerId` is derived. Let's create our first peer:

**1. Create a New Rust Project**

Open your terminal and run:

```bash
cargo new rust-p2p-node
cd rust-p2p-node
```

**2. Add `libp2p` Dependencies**

We'll use `libp2p`. We also need `tokio` for asynchronous runtime and `futures` for stream handling. Open your `Cargo.toml` file and add the following dependencies:

```toml
[dependencies]
libp2p = { version = "0.53.2", features = ["tokio", "gossipsub", "mdns", "kad", "noise", "tcp", "yamux", "identify"] }
tokio = { version = "1", features = ["full"] }
futures = "0.3.30"
```

> **Note:** We are enabling several `libp2p` features upfront (`gossipsub`, `kad`, etc.) that we will use in later steps, aligning with the architectural design for chat and discovery.

**3. Create the Peer's Identity and Core Network Component (`Swarm`)**

Now, let's write the initial code. This program will generate a new identity for our peer, print its unique `PeerId`, and set up the main networking component called a `Swarm`. The `Swarm` is responsible for managing connections and driving network events.

Replace the content of `src/main.rs` with the following:

```rust
use futures::StreamExt;
use libp2p::{
    identity,
    swarm::{SwarmBuilder, SwarmEvent},
    PeerId, Swarm,
};

// Define a custom network behavior. For now, it will be empty.
// We'll add capabilities like discovery and chat in later steps.
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "MyBehaviourEvent")]
struct MyBehaviour {
    // We will add fields here later, e.g., for Kademlia and Gossipsub.
}

// Define a placeholder for our custom event enum.
enum MyBehaviourEvent {
    // We will define event types here later.
}


#[tokio::main]
async fn main() {
    // 1. Create a new identity for the peer.
    // As per Section 4, this is a fundamental requirement.
    let local_key = identity::Keypair::generate_ed25519();
    let local_peer_id = PeerId::from(local_key.public());
    println!("Our local peer ID is: {}", local_peer_id);

    // 2. Build the transport.
    // This is the foundation for how peers will communicate.
    // We'll use TCP, as it's a standard and reliable choice.
    // The design doc also mentions QUIC and WebRTC, which libp2p supports.
    let transport = libp2p::tcp::tokio::Transport::new(libp2p::tcp::Config::default())
        .upgrade(libp2p::core::upgrade::Version::V1Lazy)
        .authenticate(libp2p::noise::Config::new(&local_key).unwrap())
        .multiplex(libp2p::yamux::Config::default())
        .boxed();

    // 3. Create an empty network behaviour.
    let behaviour = MyBehaviour {};

    // 4. Create the Swarm.
    // The Swarm manages all connections and network events for the peer.
    let mut swarm = SwarmBuilder::with_tokio_executor(transport, behaviour, local_peer_id).build();

    // 5. Tell the Swarm to listen on a random TCP port.
    // The "/ip4/0.0.0.0/tcp/0" address means we'll listen on all network interfaces
    // on a port assigned by the OS.
    swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()).unwrap();

    // 6. The main event loop.
    // The Swarm will emit events that we need to handle.
    loop {
        match swarm.select_next_some().await {
            SwarmEvent::NewListenAddr { address, .. } => {
                println!("Listening on local address: {}", address);
            }
            // We'll add more event handlers here in the next steps.
            event => {
                println!("Unhandled swarm event: {:?}", event);
            }
        }
    }
}
```

**4. Run Your First Peer**

Execute the code in your terminal:

```bash
cargo run
```

You will see output similar to this, showing your peer's unique ID and the address it's listening on:

```
Our local peer ID is: 12D3KooW...
Listening on local address: /ip4/127.0.0.1/tcp/51234
Listening on local address: /ip4/192.168.1.10/tcp/51234
```

You have now successfully created a single, isolated peer. It has an identity and is listening for incoming connections, but it doesn't know about any other peers yet.

***

### **Step 2: Two Peers Talking – Manual Discovery and Connection**

To have a network, we need at least two peers. Let's make our application able to connect to another peer if we provide its address. This demonstrates a manual connection before we automate discovery.

**1. Modify `main.rs` to Accept a Peer Address**

We'll use command-line arguments to optionally pass the address of a peer to connect to.

Update `src/main.rs`:

```rust
// ... (keep the use statements and struct definitions from before)

#[tokio::main]
async fn main() {
    // ... (keep the identity and transport creation code) ...
    let local_key = identity::Keypair::generate_ed25519();
    let local_peer_id = PeerId::from(local_key.public());
    println!("Our local peer ID is: {}", local_peer_id);

    let transport = libp2p::tcp::tokio::Transport::new(libp2p::tcp::Config::default())
        .upgrade(libp2p::core::upgrade::Version::V1Lazy)
        .authenticate(libp2p::noise::Config::new(&local_key).unwrap())
        .multiplex(libp2p::yamux::Config::default())
        .boxed();

    let behaviour = MyBehaviour {};

    let mut swarm = SwarmBuilder::with_tokio_executor(transport, behaviour, local_peer_id).build();

    swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()).unwrap();

    // New code: Check for a peer address to dial from command line arguments.
    if let Some(addr_to_dial) = std::env::args().nth(1) {
        let addr: Multiaddr = addr_to_dial.parse().expect("Failed to parse address.");
        match swarm.dial(addr.clone()) {
            Ok(_) => println!("Dialed peer at {}", addr),
            Err(e) => println!("Failed to dial peer at {}: {:?}", addr, e),
        }
    }


    // The main event loop.
    loop {
        match swarm.select_next_some().await {
            SwarmEvent::NewListenAddr { address, .. } => {
                println!("Listening on local address: {}", address);
            }
            // Add a handler for connection events.
            SwarmEvent::ConnectionEstablished { peer_id, endpoint, .. } => {
                println!("Connected to peer: {}", peer_id);
                println!("Endpoint: {:?}", endpoint.get_remote_address());
            }
            SwarmEvent::ConnectionClosed { peer_id, cause, .. } => {
                println!("Connection lost with peer: {}. Cause: {:?}", peer_id, cause);
            }
            // ... (keep the other event handler)
            event => {
                // println!("Unhandled swarm event: {:?}", event);
            }
        }
    }
}
```

> **Note:** We also need to add `use libp2p::Multiaddr;` to the `use` statements at the top of the file.

**2. Run Two Peers**

Now, we'll run two instances of our application.

* **Terminal 1 (The "Server" Peer):** Run the application without any arguments. It will start up and print its listening address.

  ```bash
  cargo run
  ```

  Note the listening address it prints, for example: `/ip4/127.0.0.1/tcp/51234`.
* **Terminal 2 (The "Client" Peer):** Run the application again, but this time, provide the listening address of the first peer as a command-line argument.

  ```bash
  # Replace the address with the one from Terminal 1
  cargo run -- /ip4/127.0.0.1/tcp/51234 
  ```

You will see log messages in both terminals indicating that a connection has been established! This is the most basic form of a P2P network.

***

### **Step 3: Automated Discovery with Kademlia DHT**

Manually providing addresses is not scalable. As your design document outlines in Section 2 and 4, a **Distributed Hash Table (DHT)** is essential for automated peer discovery. We'll implement `Kademlia`, which is a strong recommendation.

**1. Update the Network Behaviour**

First, we need to add the `Kademlia` protocol to our `MyBehaviour` struct.

Modify `src/main.rs`:

```rust
use libp2p::{
    kad::{Kademlia, KademliaEvent, store::MemoryStore},
    // ... other use statements
};

#[derive(NetworkBehaviour)]
#[behaviour(out_event = "MyBehaviourEvent")]
struct MyBehaviour {
    // Add the Kademlia behaviour.
    kad: Kademlia<MemoryStore>,
    // We'll also add Identify to help with NAT traversal later.
    identify: libp2p::identify::Behaviour,
}

// Update the event enum to include Kademlia events.
#[derive(Debug)]
enum MyBehaviourEvent {
    Kad(KademliaEvent),
    Identify(libp2p::identify::Event),
}

// Implement the conversion from the specific event to our umbrella enum.
impl From<KademliaEvent> for MyBehaviourEvent {
    fn from(event: KademliaEvent) -> Self {
        MyBehaviourEvent::Kad(event)
    }
}

impl From<libp2p::identify::Event> for MyBehaviourEvent {
    fn from(event: libp2p::identify::Event) -> Self {
        MyBehaviourEvent::Identify(event)
    }
}

// ... (main function follows)
```

**2. Integrate Kademlia into the Swarm**

Now, we instantiate `Kademlia` and add it to the `Swarm`. We will also implement the "bootstrapping" process described in Section 4. If our peer is given the address of another peer, it will connect and add it to its Kademlia routing table. This "bootstrap node" will then help our peer discover the rest of the network.

Update the `main` function in `src/main.rs`:

```rust
// ... (keep use statements and struct/enum definitions)

#[tokio::main]
async fn main() {
    // ... (keep identity and transport creation code) ...
    let local_key = identity::Keypair::generate_ed25519();
    let local_peer_id = PeerId::from(local_key.public());
    println!("Our local peer ID is: {}", local_peer_id);

    let transport = libp2p::tcp::tokio::Transport::new(libp2p::tcp::Config::default())
        .upgrade(libp2p::core::upgrade::Version::V1Lazy)
        .authenticate(libp2p::noise::Config::new(&local_key).unwrap())
        .multiplex(libp2p::yamux::Config::default())
        .boxed();

    // Create the Kademlia behaviour.
    let store = MemoryStore::new(local_peer_id);
    let kad_behaviour = Kademlia::new(local_peer_id, store);

    // Create the Identify behaviour
    let identify_behaviour = libp2p::identify::Behaviour::new(
        libp2p::identify::Config::new("p2p-chat/1.0.0".to_string(), local_key.public())
    );

    // Create our combined behaviour.
    let mut behaviour = MyBehaviour {
        kad: kad_behaviour,
        identify: identify_behaviour,
    };

    let mut swarm = SwarmBuilder::with_tokio_executor(transport, behaviour, local_peer_id).build();

    swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()).unwrap();

    // Dial the bootstrap node if one is provided.
    if let Some(addr_str) = std::env::args().nth(1) {
        let addr: Multiaddr = addr_str.parse().expect("Failed to parse address.");
        let peer_id_str = std::env::args().nth(2).expect("Please provide a peer ID.");
        let peer_id: PeerId = peer_id_str.parse().expect("Failed to parse PeerId.");
        
        swarm.behaviour_mut().kad.add_address(&peer_id, addr.clone());
        println!("Added bootstrap peer: {} at {}", peer_id, addr);
    }
    
    // Start the discovery process.
    swarm.behaviour_mut().kad.bootstrap().ok();


    // The main event loop.
    loop {
        match swarm.select_next_some().await {
            SwarmEvent::NewListenAddr { address, .. } => {
                println!("Listening on: {} with PeerId {}", address, local_peer_id);
            }
            SwarmEvent::Behaviour(MyBehaviourEvent::Kad(event)) => {
                println!("Kademlia event: {:?}", event);
            }
            SwarmEvent::Behaviour(MyBehaviourEvent::Identify(event)) => {
                println!("Identify event: {:?}", event);
                // When we identify a new peer, add them to Kademlia's routing table.
                if let libp2p::identify::Event::Received { peer_id, info } = event {
                    for addr in info.listen_addrs {
                        swarm.behaviour_mut().kad.add_address(&peer_id, addr);
                    }
                }
            }
            // ... (other handlers)
            _ => {}
        }
    }
}
```

**3. Run the Discovery Network**

* **Terminal 1 (Bootstrap Node):** Start the first peer. Note both its listening address AND its `PeerId`.

  ```bash
  cargo run
  # Output: Listening on: /ip4/127.0.0.1/tcp/55555 with PeerId 12D3KooW...
  ```
* **Terminal 2 (New Peer):** Start the second peer, giving it the address and PeerId of the first peer.

  ```bash
  # Use the address and PeerId from Terminal 1
  cargo run -- /ip4/127.0.0.1/tcp/55555 12D3KooW...
  ```

You will now see `KademliaEvent` logs in both terminals. The new peer has connected to the bootstrap node and is using the Kademlia DHT to learn about the network topology. You have successfully implemented the core of the decentralized discovery system!

### Next Steps

Following this foundation, the next logical steps based on our design would be:

1. **Implement Chat with Gossipsub:** Add the `libp2p-gossipsub` behaviour to your `MyBehaviour` struct to enable scalable, topic-based messaging, as recommended for the chat module.
2. **Handle NAT Traversal:** Integrate `libp2p-relay` to allow peers behind restrictive NATs to communicate, implementing the concepts from Section 6 of your document.
3. **Build the File-Sharing Module:** Design a protocol for advertising and transferring files, potentially using concepts from BitTorrent or IPFS as suggested.
4. **Containerize for Deployment:** Create a `Dockerfile` to package the application, simplifying deployment on Unix/Linux servers as outlined in Section 8.

This step-by-step guide has established the fundamental components of your P2P network, creating a solid, expandable base upon which you can build the full file, chat, and video sharing application.
