feat(node): wire nu-p2p via HTTP publish API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mukan Erkin TÖRÜK 2026-04-24 16:49:37 +03:00
parent c22c0e0278
commit ec8af8d1c7
5 changed files with 919 additions and 24 deletions

View file

@ -7,6 +7,16 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
## [Unreleased] ## [Unreleased]
## [0.8.0] — 2026-04-24
### Added
- `--p2p-api <url>` CLI flag — nu-p2p publish API adresi; verildiğinde P2P forwarding loop aktif olur
- `reqwest` HTTP client ile nu-p2p `/publish` endpoint'ine `BlockAnnounce` ve `TxGossip` mesajları iletilir
- `nu-node``nu-p2p` entegrasyonu tamamlandı: block loop ürettiği her blok için announce, RPC üzerinden kabul edilen her tx için gossip gönderir
### Changed
- `--p2p-addr` / `--bootstrap` flag'leri kaldırıldı; yerini `--p2p-api` aldı (process sınırı korunuyor, nu-p2p ayrı process olarak çalışıyor)
## [0.7.0] — 2026-04-24 ## [0.7.0] — 2026-04-24
### Added ### Added

View file

@ -46,6 +46,15 @@ cargo fmt && cargo clippy
## Faz Durumu ## Faz Durumu
- **Faz 0 (şu an):** Scaffold tamam. Modül sınırları tanımlı, tipler yazıldı. - **Faz 0:** ✅ Scaffold, tipler, modül sınırları.
- **Faz 1:** Tx execution (TokenTransfer önce), tek validator devnet, JSON-RPC server. - **Faz 1:** ✅ Tüm tx tipleri, block loop, JSON-RPC, P2P forwarding (`--p2p-api`).
- **Faz 2:** PoN consensus tam implementasyon, slashing, 3+ validator. - **Faz 2 (sıradaki):** Tam PoN consensus (rotation, slashing), 3+ validator testnet.
## P2P Entegrasyonu
nu-node, nu-p2p'ye `reqwest` ile HTTP POST atar. nu-p2p ayrı process olarak çalışır.
```bash
# P2P ile başlat
cargo run -- --dev --validator --p2p-api http://127.0.0.1:30334
```

879
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -46,6 +46,7 @@ sha2.workspace = true
hex.workspace = true hex.workspace = true
chrono.workspace = true chrono.workspace = true
serde_json.workspace = true serde_json.workspace = true
reqwest = { version = "0.12", features = ["json"] }
nu-consensus = { path = "crates/nu-consensus" } nu-consensus = { path = "crates/nu-consensus" }
nu-mempool = { path = "crates/nu-mempool" } nu-mempool = { path = "crates/nu-mempool" }
nu-state = { path = "crates/nu-state" } nu-state = { path = "crates/nu-state" }

View file

@ -42,13 +42,10 @@ struct Cli {
#[arg(long, default_value = "nu-devnet-1")] #[arg(long, default_value = "nu-devnet-1")]
chain_id: String, chain_id: String,
/// P2P listen address (empty = P2P disabled) /// nu-p2p publish API address (empty = P2P disabled)
/// e.g. http://127.0.0.1:30334
#[arg(long)] #[arg(long)]
p2p_addr: Option<String>, p2p_api: Option<String>,
/// Bootstrap peer multiaddrs
#[arg(long)]
bootstrap: Vec<String>,
} }
#[tokio::main] #[tokio::main]
@ -64,28 +61,43 @@ async fn main() -> Result<()> {
tracing::info!("State DB opened at {}", cli.db_path); tracing::info!("State DB opened at {}", cli.db_path);
// P2P event channel — block_loop and RPC publish, forwarded to nu-p2p swarm // P2P event channel — block_loop and RPC publish, forwarded to nu-p2p via HTTP
let (p2p_sender, rpc_p2p_tx) = if cli.p2p_addr.is_some() { let (p2p_sender, rpc_p2p_tx) = if let Some(ref api_url) = cli.p2p_api {
let (block_tx, mut block_rx) = mpsc::channel::<NodeP2pEvent>(128); let (block_tx, mut block_rx) = mpsc::channel::<NodeP2pEvent>(128);
let (rpc_tx, mut rpc_rx) = rpc_mpsc::channel::<Vec<u8>>(256); let (rpc_tx, mut rpc_rx) = rpc_mpsc::channel::<Vec<u8>>(256);
let publish_url = format!("{}/publish", api_url.trim_end_matches('/'));
let client = reqwest::Client::new();
tokio::spawn(async move { tokio::spawn(async move {
loop { loop {
tokio::select! { tokio::select! {
Some(event) = block_rx.recv() => { Some(event) = block_rx.recv() => {
match &event { let body = match &event {
NodeP2pEvent::BlockAnnounce { height, hash } => { NodeP2pEvent::BlockAnnounce { height, hash } => {
tracing::info!(height, hash = %&hash[..8], "p2p: block announce"); tracing::info!(height, hash = %&hash[..8], "p2p: block announce");
serde_json::json!({
"type": "BlockAnnounce",
"data": { "height": height, "hash": hash }
})
} }
NodeP2pEvent::TxGossip { raw_tx } => { NodeP2pEvent::TxGossip { raw_tx } => {
tracing::debug!(bytes = raw_tx.len(), "p2p: tx gossip"); tracing::debug!(bytes = raw_tx.len(), "p2p: tx gossip");
serde_json::json!({
"type": "TxGossip",
"data": { "raw_tx": raw_tx }
})
} }
} };
// TODO: forward to nu-p2p swarm let _ = client.post(&publish_url).json(&body).send().await;
} }
Some(raw) = rpc_rx.recv() => { Some(raw) = rpc_rx.recv() => {
tracing::debug!(bytes = raw.len(), "p2p: rpc tx gossip"); tracing::debug!(bytes = raw.len(), "p2p: rpc tx gossip");
// TODO: forward to nu-p2p swarm let body = serde_json::json!({
"type": "TxGossip",
"data": { "raw_tx": raw }
});
let _ = client.post(&publish_url).json(&body).send().await;
} }
else => break, else => break,
} }
@ -124,7 +136,7 @@ async fn main() -> Result<()> {
rpc_addr = %cli.rpc_addr, rpc_addr = %cli.rpc_addr,
dev = cli.dev, dev = cli.dev,
validator = cli.validator, validator = cli.validator,
p2p = cli.p2p_addr.as_deref().unwrap_or("disabled"), p2p = cli.p2p_api.as_deref().unwrap_or("disabled"),
"nu-node ready" "nu-node ready"
); );