feat(nu-node): implement nu_sendRawTx with mempool integration
- nu-block: TxPayload enum with all variants (user + auto/scheduler) - nu-mempool: PendingTx wraps RawTransaction; priority derived from TxPayload - nu-rpc: nu_sendRawTx decodes JSON tx, deduplicates, inserts into mempool - AppState: holds Arc<Mutex<Mempool>> alongside StateDb - main.rs: initializes mempool and passes to RpcServer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a42ca0f8d3
commit
fd829ba1dd
10 changed files with 213 additions and 56 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -796,6 +796,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"nu-block",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
@ -826,6 +827,9 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
|
"chrono",
|
||||||
|
"nu-block",
|
||||||
|
"nu-mempool",
|
||||||
"nu-state",
|
"nu-state",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,75 @@ pub struct BlockHeader {
|
||||||
pub slot: u32,
|
pub slot: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All transaction payload variants. Matches nu-proto TxPayload oneof.
|
||||||
|
/// "Auto" variants (NodeApprove, NftMint, NodeReject, VotingOpen) are
|
||||||
|
/// produced by the validator scheduler — never submitted by users.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
pub enum TxPayload {
|
||||||
|
TokenTransfer {
|
||||||
|
to: String,
|
||||||
|
amount: u64,
|
||||||
|
},
|
||||||
|
NodeSubmit {
|
||||||
|
story_id: String,
|
||||||
|
parent_node_id: String,
|
||||||
|
content_hash: String, // IPFS CID
|
||||||
|
temp_id: String, // client UUID, replaced at approval
|
||||||
|
entry_fee: u64,
|
||||||
|
},
|
||||||
|
VoteRegister {
|
||||||
|
node_id: String,
|
||||||
|
stake_lock: u64,
|
||||||
|
},
|
||||||
|
VoteCast {
|
||||||
|
node_id: String,
|
||||||
|
approve: bool,
|
||||||
|
},
|
||||||
|
NftTransfer {
|
||||||
|
nft_id: String,
|
||||||
|
to: String,
|
||||||
|
},
|
||||||
|
CollectionClaim {
|
||||||
|
nft_ids: Vec<String>,
|
||||||
|
},
|
||||||
|
StakeOp {
|
||||||
|
stake: bool, // true = stake, false = unstake
|
||||||
|
amount: u64,
|
||||||
|
},
|
||||||
|
ValidatorRegister {
|
||||||
|
stake: u64,
|
||||||
|
},
|
||||||
|
// --- Auto variants (validator-only) ---
|
||||||
|
NodeApprove {
|
||||||
|
temp_id: String,
|
||||||
|
canonical_id: String,
|
||||||
|
},
|
||||||
|
NftMint {
|
||||||
|
node_id: String,
|
||||||
|
recipient: String,
|
||||||
|
},
|
||||||
|
NodeReject {
|
||||||
|
node_id: String,
|
||||||
|
},
|
||||||
|
VotingOpen {
|
||||||
|
node_id: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TxPayload {
|
||||||
|
pub fn priority_hint(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::VoteCast { .. } | Self::VoteRegister { .. } => 1,
|
||||||
|
Self::NodeApprove { .. }
|
||||||
|
| Self::NftMint { .. }
|
||||||
|
| Self::NodeReject { .. }
|
||||||
|
| Self::VotingOpen { .. } => 2,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct RawTransaction {
|
pub struct RawTransaction {
|
||||||
pub tx_id: String,
|
pub tx_id: String,
|
||||||
|
|
@ -20,7 +89,7 @@ pub struct RawTransaction {
|
||||||
pub nonce: u64,
|
pub nonce: u64,
|
||||||
pub fee: u64,
|
pub fee: u64,
|
||||||
pub sig: Vec<u8>,
|
pub sig: Vec<u8>,
|
||||||
pub payload: serde_json::Value,
|
pub payload: TxPayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|
@ -31,7 +100,7 @@ pub struct Block {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct TxReceipt {
|
pub struct TxReceipt {
|
||||||
pub tx_id: String,
|
pub tx_id: String,
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
pub error: String,
|
pub error: String,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,4 @@ anyhow.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
|
nu-block = { path = "../nu-block" }
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@ pub mod pool;
|
||||||
pub mod priority;
|
pub mod priority;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use pool::Mempool;
|
pub use pool::{Mempool, PendingTx};
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use crate::types::*;
|
|
||||||
use crate::priority::TxPriority;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use nu_block::types::RawTransaction;
|
||||||
|
|
||||||
|
use crate::priority::TxPriority;
|
||||||
|
use crate::types::{MEMPOOL_MAX_TX, MEMPOOL_TTL_MS, MAX_TX_PER_SENDER};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PendingTx {
|
pub struct PendingTx {
|
||||||
pub tx_id: String,
|
pub tx: RawTransaction,
|
||||||
pub sender: String,
|
pub priority: TxPriority,
|
||||||
pub fee: u64,
|
|
||||||
pub priority: TxPriority,
|
|
||||||
pub received_at: i64, // Unix epoch ms
|
pub received_at: i64, // Unix epoch ms
|
||||||
pub raw: Vec<u8>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Mempool {
|
pub struct Mempool {
|
||||||
|
|
@ -19,50 +19,62 @@ pub struct Mempool {
|
||||||
|
|
||||||
impl Mempool {
|
impl Mempool {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self { txs: vec![], sender_counts: HashMap::new() }
|
||||||
txs: vec![],
|
|
||||||
sender_counts: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, tx: PendingTx) -> bool {
|
pub fn insert(&mut self, tx: RawTransaction, now_ms: i64) -> bool {
|
||||||
if self.txs.len() >= MEMPOOL_MAX_TX {
|
if self.txs.len() >= MEMPOOL_MAX_TX {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
let priority = TxPriority::from_payload(&tx.payload);
|
||||||
let count = self.sender_counts.entry(tx.sender.clone()).or_insert(0);
|
let count = self.sender_counts.entry(tx.sender.clone()).or_insert(0);
|
||||||
if *count >= MAX_TX_PER_SENDER && tx.priority == TxPriority::Normal {
|
if *count >= MAX_TX_PER_SENDER && priority == TxPriority::Normal {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*count += 1;
|
*count += 1;
|
||||||
self.txs.push(tx);
|
self.txs.push(PendingTx { tx, priority, received_at: now_ms });
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, tx_id: &str) -> bool {
|
||||||
|
self.txs.iter().any(|p| p.tx.tx_id == tx_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn select_for_block(&mut self, max_tx: usize, now_ms: i64) -> Vec<PendingTx> {
|
pub fn select_for_block(&mut self, max_tx: usize, now_ms: i64) -> Vec<PendingTx> {
|
||||||
self.evict_expired(now_ms);
|
self.evict_expired(now_ms);
|
||||||
|
|
||||||
let mut sorted = self.txs.clone();
|
let mut sorted = self.txs.clone();
|
||||||
sorted.sort_by(|a, b| {
|
sorted.sort_by(|a, b| {
|
||||||
b.priority.cmp(&a.priority)
|
b.priority.cmp(&a.priority).then(b.tx.fee.cmp(&a.tx.fee))
|
||||||
.then(b.fee.cmp(&a.fee))
|
|
||||||
});
|
});
|
||||||
sorted.into_iter().take(max_tx).collect()
|
sorted.into_iter().take(max_tx).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, tx_id: &str) {
|
pub fn remove(&mut self, tx_id: &str) {
|
||||||
if let Some(pos) = self.txs.iter().position(|t| t.tx_id == tx_id) {
|
if let Some(pos) = self.txs.iter().position(|p| p.tx.tx_id == tx_id) {
|
||||||
let tx = self.txs.remove(pos);
|
let removed = self.txs.remove(pos);
|
||||||
if let Some(c) = self.sender_counts.get_mut(&tx.sender) {
|
if let Some(c) = self.sender_counts.get_mut(&removed.tx.sender) {
|
||||||
*c = c.saturating_sub(1);
|
*c = c.saturating_sub(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evict_expired(&mut self, now_ms: i64) {
|
|
||||||
self.txs.retain(|t| now_ms - t.received_at < MEMPOOL_TTL_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.txs.len()
|
self.txs.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn evict_expired(&mut self, now_ms: i64) {
|
||||||
|
let mut removed_senders: Vec<String> = vec![];
|
||||||
|
self.txs.retain(|p| {
|
||||||
|
let keep = now_ms - p.received_at < MEMPOOL_TTL_MS;
|
||||||
|
if !keep {
|
||||||
|
removed_senders.push(p.tx.sender.clone());
|
||||||
|
}
|
||||||
|
keep
|
||||||
|
});
|
||||||
|
for sender in removed_senders {
|
||||||
|
if let Some(c) = self.sender_counts.get_mut(&sender) {
|
||||||
|
*c = c.saturating_sub(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,21 @@
|
||||||
// NodeApprove and VoteCast transactions are prioritized over regular transfers.
|
use nu_block::types::TxPayload;
|
||||||
// Within same priority tier, higher fee wins.
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum TxPriority {
|
pub enum TxPriority {
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
High = 1, // VoteCast, VoteRegister
|
High = 1, // VoteCast, VoteRegister
|
||||||
Critical = 2, // NodeApprove, VotingOpen, NodeReject (auto-scheduler)
|
Critical = 2, // NodeApprove, NftMint, NodeReject, VotingOpen (auto-scheduler)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TxPriority {
|
||||||
|
pub fn from_payload(payload: &TxPayload) -> Self {
|
||||||
|
match payload {
|
||||||
|
TxPayload::VoteCast { .. } | TxPayload::VoteRegister { .. } => Self::High,
|
||||||
|
TxPayload::NodeApprove { .. }
|
||||||
|
| TxPayload::NftMint { .. }
|
||||||
|
| TxPayload::NodeReject { .. }
|
||||||
|
| TxPayload::VotingOpen { .. } => Self::Critical,
|
||||||
|
_ => Self::Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,7 @@ serde_json.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
axum.workspace = true
|
axum.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
nu-state = { path = "../nu-state" }
|
nu-state = { path = "../nu-state" }
|
||||||
|
nu-mempool = { path = "../nu-mempool" }
|
||||||
|
nu-block = { path = "../nu-block" }
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ use crate::{
|
||||||
server::AppState,
|
server::AppState,
|
||||||
types::{JsonRpcRequest, JsonRpcResponse},
|
types::{JsonRpcRequest, JsonRpcResponse},
|
||||||
};
|
};
|
||||||
|
use nu_block::types::RawTransaction;
|
||||||
use nu_state::account::AccountState;
|
use nu_state::account::AccountState;
|
||||||
|
|
||||||
pub fn dispatch(req: JsonRpcRequest, state: &AppState) -> JsonRpcResponse {
|
pub async fn dispatch(req: JsonRpcRequest, state: &AppState) -> JsonRpcResponse {
|
||||||
match req.method.as_str() {
|
match req.method.as_str() {
|
||||||
"nu_chainInfo" => handle_chain_info(&req, state),
|
"nu_chainInfo" => handle_chain_info(&req, state),
|
||||||
"nu_getAccount" => handle_get_account(&req, state),
|
"nu_getAccount" => handle_get_account(&req, state),
|
||||||
"nu_sendRawTx" => handle_send_raw_tx(&req, state),
|
"nu_sendRawTx" => handle_send_raw_tx(&req, state).await,
|
||||||
"nu_getBlock" => not_implemented(&req, "nu_getBlock"),
|
"nu_getBlock" => not_implemented(&req, "nu_getBlock"),
|
||||||
"nu_getTx" => not_implemented(&req, "nu_getTx"),
|
"nu_getTx" => not_implemented(&req, "nu_getTx"),
|
||||||
"nu_getStory" => not_implemented(&req, "nu_getStory"),
|
"nu_getStory" => not_implemented(&req, "nu_getStory"),
|
||||||
|
|
@ -26,7 +27,7 @@ fn handle_chain_info(req: &JsonRpcRequest, state: &AppState) -> JsonRpcResponse
|
||||||
JsonRpcResponse::ok(
|
JsonRpcResponse::ok(
|
||||||
req.id.clone(),
|
req.id.clone(),
|
||||||
json!({
|
json!({
|
||||||
"chain_id": state.chain_id,
|
"chain_id": state.chain_id,
|
||||||
"node_version": env!("CARGO_PKG_VERSION"),
|
"node_version": env!("CARGO_PKG_VERSION"),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -40,9 +41,10 @@ fn handle_get_account(req: &JsonRpcRequest, state: &AppState) -> JsonRpcResponse
|
||||||
|
|
||||||
let key = format!("account:{address}");
|
let key = format!("account:{address}");
|
||||||
match state.db.get::<AccountState>(&key) {
|
match state.db.get::<AccountState>(&key) {
|
||||||
Ok(Some(account)) => JsonRpcResponse::ok(req.id.clone(), serde_json::to_value(account).unwrap()),
|
Ok(Some(account)) => {
|
||||||
|
JsonRpcResponse::ok(req.id.clone(), serde_json::to_value(account).unwrap())
|
||||||
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
// Return empty account — address exists conceptually with zero balance
|
|
||||||
let empty = AccountState::new(address);
|
let empty = AccountState::new(address);
|
||||||
JsonRpcResponse::ok(req.id.clone(), serde_json::to_value(empty).unwrap())
|
JsonRpcResponse::ok(req.id.clone(), serde_json::to_value(empty).unwrap())
|
||||||
}
|
}
|
||||||
|
|
@ -50,19 +52,52 @@ fn handle_get_account(req: &JsonRpcRequest, state: &AppState) -> JsonRpcResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_send_raw_tx(req: &JsonRpcRequest, _state: &AppState) -> JsonRpcResponse {
|
async fn handle_send_raw_tx(req: &JsonRpcRequest, state: &AppState) -> JsonRpcResponse {
|
||||||
// Faz 1: accepts tx hex, decodes, validates, adds to mempool
|
let raw_value = match req.params.get(0) {
|
||||||
// For now: echo back a stub tx_id so platform integration can proceed
|
Some(v) => v.clone(),
|
||||||
let _raw = match req.params.get(0).and_then(|v| v.as_str()) {
|
None => {
|
||||||
Some(r) => r,
|
return JsonRpcResponse::err(req.id.clone(), -32602, "Missing tx param".into())
|
||||||
None => return JsonRpcResponse::err(req.id.clone(), -32602, "Missing raw tx param".into()),
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
JsonRpcResponse::err(
|
let tx: RawTransaction = match serde_json::from_value(raw_value) {
|
||||||
req.id.clone(),
|
Ok(t) => t,
|
||||||
-32000,
|
Err(e) => {
|
||||||
"nu_sendRawTx: mempool integration pending (Faz 1)".into(),
|
return JsonRpcResponse::err(
|
||||||
)
|
req.id.clone(),
|
||||||
|
-32602,
|
||||||
|
format!("Invalid tx format: {e}"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Basic structural validation before touching mempool
|
||||||
|
if tx.tx_id.is_empty() {
|
||||||
|
return JsonRpcResponse::err(req.id.clone(), -32602, "tx_id is required".into());
|
||||||
|
}
|
||||||
|
if tx.sender.is_empty() {
|
||||||
|
return JsonRpcResponse::err(req.id.clone(), -32602, "sender is required".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx_id = tx.tx_id.clone();
|
||||||
|
let now_ms = chrono::Utc::now().timestamp_millis();
|
||||||
|
|
||||||
|
let mut pool = state.mempool.lock().await;
|
||||||
|
|
||||||
|
if pool.contains(&tx_id) {
|
||||||
|
return JsonRpcResponse::err(req.id.clone(), -32000, "tx already in mempool".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if pool.insert(tx, now_ms) {
|
||||||
|
tracing::info!("tx accepted into mempool: {tx_id} (pool size: {})", pool.len());
|
||||||
|
JsonRpcResponse::ok(req.id.clone(), json!({ "tx_id": tx_id }))
|
||||||
|
} else {
|
||||||
|
JsonRpcResponse::err(
|
||||||
|
req.id.clone(),
|
||||||
|
-32000,
|
||||||
|
"mempool full or sender limit reached".into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn not_implemented(req: &JsonRpcRequest, method: &str) -> JsonRpcResponse {
|
fn not_implemented(req: &JsonRpcRequest, method: &str) -> JsonRpcResponse {
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,18 @@ use axum::{
|
||||||
routing::post,
|
routing::post,
|
||||||
Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
use tokio::net::TcpListener;
|
use tokio::{net::TcpListener, sync::Mutex};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
handlers::dispatch,
|
handlers::dispatch,
|
||||||
types::{JsonRpcRequest, JsonRpcResponse},
|
types::{JsonRpcRequest, JsonRpcResponse},
|
||||||
};
|
};
|
||||||
|
use nu_mempool::Mempool;
|
||||||
use nu_state::db::StateDb;
|
use nu_state::db::StateDb;
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub db: Arc<StateDb>,
|
pub db: Arc<StateDb>,
|
||||||
|
pub mempool: Arc<Mutex<Mempool>>,
|
||||||
pub chain_id: String,
|
pub chain_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,10 +28,15 @@ pub struct RpcServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RpcServer {
|
impl RpcServer {
|
||||||
pub fn new(bind_addr: impl Into<String>, db: Arc<StateDb>, chain_id: String) -> Self {
|
pub fn new(
|
||||||
|
bind_addr: impl Into<String>,
|
||||||
|
db: Arc<StateDb>,
|
||||||
|
mempool: Arc<Mutex<Mempool>>,
|
||||||
|
chain_id: String,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bind_addr: bind_addr.into(),
|
bind_addr: bind_addr.into(),
|
||||||
state: Arc::new(AppState { db, chain_id }),
|
state: Arc::new(AppState { db, mempool, chain_id }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,6 +68,6 @@ async fn rpc_handler(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let resp = dispatch(req, &state);
|
let resp = dispatch(req, &state).await;
|
||||||
(StatusCode::OK, Json(resp))
|
(StatusCode::OK, Json(resp))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/main.rs
18
src/main.rs
|
|
@ -2,8 +2,10 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
use nu_mempool::Mempool;
|
||||||
use nu_rpc::server::RpcServer;
|
use nu_rpc::server::RpcServer;
|
||||||
use nu_state::StateDb;
|
use nu_state::StateDb;
|
||||||
|
|
||||||
|
|
@ -46,8 +48,20 @@ async fn main() -> Result<()> {
|
||||||
let db = Arc::new(StateDb::open(&cli.db_path)?);
|
let db = Arc::new(StateDb::open(&cli.db_path)?);
|
||||||
tracing::info!("State DB opened at {}", cli.db_path);
|
tracing::info!("State DB opened at {}", cli.db_path);
|
||||||
|
|
||||||
let rpc = RpcServer::new(cli.rpc_addr.clone(), Arc::clone(&db), cli.chain_id.clone());
|
let mempool = Arc::new(Mutex::new(Mempool::new()));
|
||||||
tracing::info!("nu-node ready — chain_id={} rpc={}", cli.chain_id, cli.rpc_addr);
|
|
||||||
|
let rpc = RpcServer::new(
|
||||||
|
cli.rpc_addr.clone(),
|
||||||
|
Arc::clone(&db),
|
||||||
|
Arc::clone(&mempool),
|
||||||
|
cli.chain_id.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
"nu-node ready — chain_id={} rpc={}",
|
||||||
|
cli.chain_id,
|
||||||
|
cli.rpc_addr
|
||||||
|
);
|
||||||
|
|
||||||
rpc.run().await?;
|
rpc.run().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue