feat(nu-p2p): initial Faz 0 scaffold
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
9cd556a439
9 changed files with 178 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
target/
|
||||||
|
*.rs.bk
|
||||||
|
.env
|
||||||
35
CLAUDE.md
Normal file
35
CLAUDE.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# nu-p2p — CLAUDE.md
|
||||||
|
|
||||||
|
P2P ağ katmanı. `nu-node`'dan ayrı tutulur — test ederken network mock'lanabilir.
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- `libp2p 0.54` — Gossipsub, Kademlia DHT, Identify, Ping
|
||||||
|
- Tokio async runtime
|
||||||
|
|
||||||
|
## Modüller
|
||||||
|
|
||||||
|
| Dosya | Sorumluluk |
|
||||||
|
|-------|-----------|
|
||||||
|
| `behaviour.rs` | libp2p `NetworkBehaviour` kompozisyonu |
|
||||||
|
| `messages.rs` | Gossip topic sabitleri + mesaj tipleri |
|
||||||
|
| `peer.rs` | PeerRegistry (max 50 bağlantı) |
|
||||||
|
| `config.rs` | Listen addr, bootstrap peers, max peers |
|
||||||
|
|
||||||
|
## Gossip Topics
|
||||||
|
|
||||||
|
```
|
||||||
|
nu/blocks/1 → BlockAnnounce, BlockRequest, BlockResponse
|
||||||
|
nu/txs/1 → TxGossip
|
||||||
|
nu/votes/1 → VoteAnnounce
|
||||||
|
nu/validators/1 → ValidatorHeartbeat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Geliştirme
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --bin nu-p2p
|
||||||
|
|
||||||
|
# Faz 1: iki node arası gossip testi
|
||||||
|
RUST_LOG=debug cargo run --bin nu-p2p -- --bootstrap /ip4/127.0.0.1/tcp/30333
|
||||||
|
```
|
||||||
28
Cargo.toml
Normal file
28
Cargo.toml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
[package]
|
||||||
|
name = "nu-p2p"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nu-p2p"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
libp2p = { version = "0.54", features = [
|
||||||
|
"tokio",
|
||||||
|
"tcp",
|
||||||
|
"noise",
|
||||||
|
"yamux",
|
||||||
|
"gossipsub",
|
||||||
|
"kad",
|
||||||
|
"identify",
|
||||||
|
"ping",
|
||||||
|
] }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
anyhow = "1"
|
||||||
|
thiserror = "1"
|
||||||
|
tracing = "1"
|
||||||
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
futures = "0.3"
|
||||||
13
src/behaviour.rs
Normal file
13
src/behaviour.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// libp2p combined behaviour: Gossipsub + Kademlia DHT + Identify + Ping
|
||||||
|
// Full implementation in Faz 1; scaffold defines the composited behaviour struct.
|
||||||
|
|
||||||
|
use libp2p::{gossipsub, identify, kad, ping};
|
||||||
|
use libp2p::swarm::NetworkBehaviour;
|
||||||
|
|
||||||
|
#[derive(NetworkBehaviour)]
|
||||||
|
pub struct NuBehaviour {
|
||||||
|
pub gossipsub: gossipsub::Behaviour,
|
||||||
|
pub kademlia: kad::Behaviour<kad::store::MemoryStore>,
|
||||||
|
pub identify: identify::Behaviour,
|
||||||
|
pub ping: ping::Behaviour,
|
||||||
|
}
|
||||||
20
src/config.rs
Normal file
20
src/config.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub const MAX_PEERS: usize = 50;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct P2pConfig {
|
||||||
|
pub listen_addr: String, // e.g. "/ip4/0.0.0.0/tcp/30333"
|
||||||
|
pub bootstrap_peers: Vec<String>, // multiaddrs from genesis.toml
|
||||||
|
pub max_peers: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for P2pConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
listen_addr: "/ip4/0.0.0.0/tcp/30333".into(),
|
||||||
|
bootstrap_peers: vec![],
|
||||||
|
max_peers: MAX_PEERS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/lib.rs
Normal file
4
src/lib.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod behaviour;
|
||||||
|
pub mod messages;
|
||||||
|
pub mod peer;
|
||||||
|
pub mod config;
|
||||||
14
src/main.rs
Normal file
14
src/main.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_env_filter(EnvFilter::from_default_env())
|
||||||
|
.init();
|
||||||
|
|
||||||
|
tracing::info!("nu-p2p starting...");
|
||||||
|
|
||||||
|
// TODO Faz 1: build libp2p swarm with NuBehaviour, connect bootstrap peers, run event loop
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
19
src/messages.rs
Normal file
19
src/messages.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// All gossip topics on the network.
|
||||||
|
pub const TOPIC_BLOCKS: &str = "nu/blocks/1";
|
||||||
|
pub const TOPIC_TXS: &str = "nu/txs/1";
|
||||||
|
pub const TOPIC_VOTES: &str = "nu/votes/1";
|
||||||
|
pub const TOPIC_VALIDATORS: &str = "nu/validators/1";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "type", content = "data")]
|
||||||
|
pub enum NetworkMessage {
|
||||||
|
BlockAnnounce { height: u64, hash: String },
|
||||||
|
BlockRequest { height: u64 },
|
||||||
|
BlockResponse { raw_block: Vec<u8> },
|
||||||
|
TxGossip { raw_tx: Vec<u8> },
|
||||||
|
PeerExchange { peers: Vec<String> }, // multiaddrs
|
||||||
|
ValidatorHeartbeat { address: String, slot: u32 },
|
||||||
|
VoteAnnounce { node_id: String, voter: String, approve: bool },
|
||||||
|
}
|
||||||
42
src/peer.rs
Normal file
42
src/peer.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PeerInfo {
|
||||||
|
pub peer_id: String,
|
||||||
|
pub addr: String,
|
||||||
|
pub is_validator: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PeerRegistry {
|
||||||
|
peers: Vec<PeerInfo>,
|
||||||
|
max: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PeerRegistry {
|
||||||
|
pub fn new(max: usize) -> Self {
|
||||||
|
Self { peers: vec![], max }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, peer: PeerInfo) -> bool {
|
||||||
|
if self.peers.len() >= self.max {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !self.peers.iter().any(|p| p.peer_id == peer.peer_id) {
|
||||||
|
self.peers.push(peer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, peer_id: &str) {
|
||||||
|
self.peers.retain(|p| p.peer_id != peer_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.peers.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validator_peers(&self) -> Vec<&PeerInfo> {
|
||||||
|
self.peers.iter().filter(|p| p.is_validator).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue