feat(node): wire P2P gossip channel to RPC sendRawTx and block announce
This commit is contained in:
parent
04e5f3d166
commit
9837edfb1f
4 changed files with 58 additions and 19 deletions
|
|
@ -7,6 +7,14 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.4.0] — 2026-04-24
|
||||
|
||||
### Added
|
||||
- `src/p2p.rs` — `P2pSender` ve `NodeP2pEvent` (BlockAnnounce, TxGossip); block loop ve RPC handler'larının P2P event'i yayınlamasını sağlar
|
||||
- `--p2p-addr` ve `--bootstrap` CLI flag'leri — P2P etkinleştirildiğinde event channel başlatılır
|
||||
- `RpcServer::with_p2p` constructor — RPC katmanına P2P gossip sender enjekte eder
|
||||
- `nu_sendRawTx`: tx kabul edildiğinde raw bytes P2P gossip channel'ına iletilir
|
||||
|
||||
## [0.3.0] — 2026-04-24
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ async fn handle_send_raw_tx(req: &JsonRpcRequest, state: &AppState) -> JsonRpcRe
|
|||
None => return JsonRpcResponse::err(req.id.clone(), -32602, "Missing tx param".into()),
|
||||
};
|
||||
|
||||
let tx: RawTransaction = match serde_json::from_value(raw_value) {
|
||||
let tx: RawTransaction = match serde_json::from_value(raw_value.clone()) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return JsonRpcResponse::err(req.id.clone(), -32602, format!("Invalid tx format: {e}"))
|
||||
|
|
@ -84,6 +84,11 @@ async fn handle_send_raw_tx(req: &JsonRpcRequest, state: &AppState) -> JsonRpcRe
|
|||
|
||||
if pool.insert(tx, now_ms) {
|
||||
tracing::info!("tx accepted: {tx_id} (pool size: {})", pool.len());
|
||||
if let Some(ref p2p_tx) = state.p2p_tx {
|
||||
if let Ok(raw) = serde_json::to_vec(&raw_value) {
|
||||
let _ = p2p_tx.try_send(raw);
|
||||
}
|
||||
}
|
||||
JsonRpcResponse::ok(req.id.clone(), json!({ "tx_id": tx_id }))
|
||||
} else {
|
||||
JsonRpcResponse::err(req.id.clone(), -32000, "mempool full or sender limit reached".into())
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use axum::{
|
|||
routing::post,
|
||||
Json, Router,
|
||||
};
|
||||
use tokio::{net::TcpListener, sync::Mutex};
|
||||
use tokio::{net::TcpListener, sync::{mpsc, Mutex}};
|
||||
|
||||
use crate::{
|
||||
handlers::dispatch,
|
||||
|
|
@ -17,9 +17,11 @@ use nu_mempool::Mempool;
|
|||
use nu_state::StateDb;
|
||||
|
||||
pub struct AppState {
|
||||
pub db: Arc<Mutex<StateDb>>,
|
||||
pub mempool: Arc<Mutex<Mempool>>,
|
||||
pub chain_id: String,
|
||||
pub db: Arc<Mutex<StateDb>>,
|
||||
pub mempool: Arc<Mutex<Mempool>>,
|
||||
pub chain_id: String,
|
||||
/// Raw serialized tx bytes forwarded to P2P gossip on acceptance
|
||||
pub p2p_tx: Option<mpsc::Sender<Vec<u8>>>,
|
||||
}
|
||||
|
||||
pub struct RpcServer {
|
||||
|
|
@ -33,10 +35,20 @@ impl RpcServer {
|
|||
db: Arc<Mutex<StateDb>>,
|
||||
mempool: Arc<Mutex<Mempool>>,
|
||||
chain_id: String,
|
||||
) -> Self {
|
||||
Self::with_p2p(bind_addr, db, mempool, chain_id, None)
|
||||
}
|
||||
|
||||
pub fn with_p2p(
|
||||
bind_addr: impl Into<String>,
|
||||
db: Arc<Mutex<StateDb>>,
|
||||
mempool: Arc<Mutex<Mempool>>,
|
||||
chain_id: String,
|
||||
p2p_tx: Option<mpsc::Sender<Vec<u8>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
bind_addr: bind_addr.into(),
|
||||
state: Arc::new(AppState { db, mempool, chain_id }),
|
||||
state: Arc::new(AppState { db, mempool, chain_id, p2p_tx }),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
40
src/main.rs
40
src/main.rs
|
|
@ -13,6 +13,7 @@ use nu_rpc::server::RpcServer;
|
|||
use nu_state::StateDb;
|
||||
|
||||
use p2p::{NodeP2pEvent, P2pSender};
|
||||
use tokio::sync::mpsc as rpc_mpsc;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "nu-node", version)]
|
||||
|
|
@ -63,25 +64,37 @@ async fn main() -> Result<()> {
|
|||
|
||||
tracing::info!("State DB opened at {}", cli.db_path);
|
||||
|
||||
// P2P event channel — block_loop publishes, this node forwards to nu-p2p
|
||||
let p2p_sender = if cli.p2p_addr.is_some() {
|
||||
let (tx, mut rx) = mpsc::channel::<NodeP2pEvent>(128);
|
||||
// P2P event channel — block_loop and RPC publish, forwarded to nu-p2p swarm
|
||||
let (p2p_sender, rpc_p2p_tx) = if cli.p2p_addr.is_some() {
|
||||
let (block_tx, mut block_rx) = mpsc::channel::<NodeP2pEvent>(128);
|
||||
let (rpc_tx, mut rpc_rx) = rpc_mpsc::channel::<Vec<u8>>(256);
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(event) = rx.recv().await {
|
||||
match &event {
|
||||
NodeP2pEvent::BlockAnnounce { height, hash } => {
|
||||
tracing::info!(height, hash = %&hash[..8], "p2p: block announce");
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some(event) = block_rx.recv() => {
|
||||
match &event {
|
||||
NodeP2pEvent::BlockAnnounce { height, hash } => {
|
||||
tracing::info!(height, hash = %&hash[..8], "p2p: block announce");
|
||||
}
|
||||
NodeP2pEvent::TxGossip { raw_tx } => {
|
||||
tracing::debug!(bytes = raw_tx.len(), "p2p: tx gossip");
|
||||
}
|
||||
}
|
||||
// TODO: forward to nu-p2p swarm
|
||||
}
|
||||
NodeP2pEvent::TxGossip { raw_tx } => {
|
||||
tracing::debug!(bytes = raw_tx.len(), "p2p: tx gossip");
|
||||
Some(raw) = rpc_rx.recv() => {
|
||||
tracing::debug!(bytes = raw.len(), "p2p: rpc tx gossip");
|
||||
// TODO: forward to nu-p2p swarm
|
||||
}
|
||||
else => break,
|
||||
}
|
||||
// TODO Faz 1: forward to nu-p2p swarm via IPC or embedded swarm
|
||||
}
|
||||
});
|
||||
Some(P2pSender(tx))
|
||||
|
||||
(Some(P2pSender(block_tx)), Some(rpc_tx))
|
||||
} else {
|
||||
None
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if cli.dev && cli.validator {
|
||||
|
|
@ -97,11 +110,12 @@ async fn main() -> Result<()> {
|
|||
));
|
||||
}
|
||||
|
||||
let rpc = RpcServer::new(
|
||||
let rpc = RpcServer::with_p2p(
|
||||
cli.rpc_addr.clone(),
|
||||
Arc::clone(&db),
|
||||
Arc::clone(&mempool),
|
||||
cli.chain_id.clone(),
|
||||
rpc_p2p_tx,
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue