Skip to content

Commit ec0d770

Browse files
committed
Not Yet Done
1 parent 1654b42 commit ec0d770

27 files changed

Lines changed: 2185 additions & 122 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ secrets/
3131

3232
# Benchmarks
3333
criterion/
34+
.bitcell/
35+
help_output.txt
36+
target/

cleanup.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
3+
# Quick manual cleanup - just delete the state file and you're done!
4+
5+
echo "🧹 Clearing BitCell admin state..."
6+
7+
# Kill existing processes
8+
echo "Killing existing BitCell processes..."
9+
pkill -f bitcell-node || true
10+
pkill -f bitcell-admin || true
11+
# Wait for ports to free up
12+
sleep 2
13+
14+
# Clear admin deployment state
15+
rm -rf .bitcell/admin/*
16+
17+
# Clear temp node data
18+
rm -rf /tmp/bitcell/*
19+
20+
echo "✅ State cleared!"
21+
echo ""
22+
echo "Now restart admin console:"
23+
cargo run --release -p bitcell-admin

crates/bitcell-admin/src/api/metrics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use axum::{
55
http::StatusCode,
66
Json,
77
};
8-
use serde::{Deserialize, Serialize};
8+
use serde::Serialize;
99
use std::sync::Arc;
1010

1111
use crate::AppState;

crates/bitcell-admin/src/api/nodes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
99
use std::sync::Arc;
1010

1111
use crate::AppState;
12-
use super::{NodeInfo, NodeStatus};
12+
use super::NodeInfo;
1313

1414
#[derive(Debug, Serialize)]
1515
pub struct NodesResponse {

crates/bitcell-admin/src/deployment.rs

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ use std::sync::Arc;
44

55
use crate::api::NodeType;
66
use crate::process::{ProcessManager, NodeConfig};
7+
use crate::setup::{SetupManager, NodeEndpoint};
78

89
pub struct DeploymentManager {
910
process: Arc<ProcessManager>,
11+
setup: Arc<SetupManager>,
1012
}
1113

1214
impl DeploymentManager {
13-
pub fn new(process: Arc<ProcessManager>) -> Self {
14-
Self { process }
15+
pub fn new(process: Arc<ProcessManager>, setup: Arc<SetupManager>) -> Self {
16+
Self { process, setup }
1517
}
1618

1719
pub async fn deploy_nodes(&self, deployment_id: &str, node_type: NodeType, count: usize) {
@@ -22,29 +24,91 @@ impl DeploymentManager {
2224
node_type
2325
);
2426

25-
let base_port = match node_type {
26-
NodeType::Validator => 9000,
27-
NodeType::Miner => 9100,
28-
NodeType::FullNode => 9200,
27+
// Find the highest used port to avoid conflicts
28+
// Using higher ports (19000+) to avoid conflicts with system services
29+
let mut base_port = match node_type {
30+
NodeType::Validator => 19000,
31+
NodeType::Miner => 19100,
32+
NodeType::FullNode => 19200,
2933
};
34+
35+
// Check existing nodes in process manager
36+
let existing_nodes = self.process.list_nodes();
37+
for node in &existing_nodes {
38+
if node.port >= base_port {
39+
// We use port (P2P) and port+1 (Metrics), so next available is port+2
40+
base_port = std::cmp::max(base_port, node.port + 2);
41+
}
42+
}
43+
44+
// Check existing nodes in setup manager
45+
let setup_nodes = self.setup.get_nodes();
46+
for node in &setup_nodes {
47+
// Parse port from metrics endpoint if possible, or just skip
48+
// This is a heuristic
49+
if let Some(port_str) = node.metrics_endpoint.split(':').last() {
50+
if let Some(port_part) = port_str.split('/').next() {
51+
if let Ok(metrics_port) = port_part.parse::<u16>() {
52+
// metrics_port is port + 1, so P2P port is metrics_port - 1
53+
let p2p_port = metrics_port.saturating_sub(1);
54+
if p2p_port >= base_port {
55+
base_port = std::cmp::max(base_port, p2p_port + 2);
56+
}
57+
}
58+
}
59+
}
60+
}
3061

3162
let base_rpc_port = base_port + 1000;
3263

3364
for i in 0..count {
3465
let node_id = format!("{:?}-{}-{}", node_type, deployment_id, i);
66+
// Increment by 2 to allow space for metrics port (port + 1)
67+
let port = base_port + (i * 2) as u16;
68+
let rpc_port = base_rpc_port + i as u16;
69+
3570
let config = NodeConfig {
3671
node_type,
3772
data_dir: format!("/tmp/bitcell/{}", node_id),
38-
port: base_port + i as u16,
39-
rpc_port: base_rpc_port + i as u16,
73+
port,
74+
rpc_port,
4075
log_level: "info".to_string(),
4176
network: "testnet".to_string(),
4277
};
4378

4479
// Register the node (but don't start it automatically)
80+
// Note: The UI calls start_node separately, or we could start it here.
81+
// But wait, the UI "Deploy" button calls deploy_node, which spawns this task.
82+
// The UI then refreshes the list. It doesn't automatically start them?
83+
// The screenshot shows them as "Running".
84+
// Let's check process.rs start_node again.
85+
// Ah, register_node returns NodeStatus::Stopped.
86+
// So the user must have clicked Start.
87+
// But wait, if I register them in SetupManager, they appear in the list.
88+
4589
self.process.register_node(node_id.clone(), config);
90+
91+
// Register in SetupManager so metrics can be fetched
92+
let endpoint = NodeEndpoint {
93+
id: node_id.clone(),
94+
node_type: format!("{:?}", node_type).to_lowercase(),
95+
metrics_endpoint: format!("http://127.0.0.1:{}/metrics", port + 1),
96+
rpc_endpoint: format!("http://127.0.0.1:{}", rpc_port),
97+
};
98+
self.setup.add_node(endpoint);
4699

47100
tracing::info!("Registered node '{}' in deployment {}", node_id, deployment_id);
101+
102+
// Auto-start the node for convenience
103+
if let Err(e) = self.process.start_node(&node_id) {
104+
tracing::error!("Failed to auto-start node {}: {}", node_id, e);
105+
}
106+
}
107+
108+
// Save setup state
109+
let setup_path = std::path::PathBuf::from(crate::setup::SETUP_FILE_PATH);
110+
if let Err(e) = self.setup.save_to_file(&setup_path) {
111+
tracing::error!("Failed to save setup state: {}", e);
48112
}
49113

50114
tracing::info!(

crates/bitcell-admin/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ impl AdminConsole {
4747
/// Create a new admin console
4848
pub fn new(addr: SocketAddr) -> Self {
4949
let process = Arc::new(ProcessManager::new());
50-
let deployment = Arc::new(DeploymentManager::new(process.clone()));
5150
let setup = Arc::new(setup::SetupManager::new());
51+
let deployment = Arc::new(DeploymentManager::new(process.clone(), setup.clone()));
5252

5353
// Try to load setup state from default location
5454
let setup_path = std::path::PathBuf::from(SETUP_FILE_PATH);

crates/bitcell-admin/src/metrics_client.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,19 @@ impl MetricsClient {
3131
pub fn new() -> Self {
3232
Self {
3333
client: reqwest::Client::builder()
34-
.timeout(Duration::from_secs(10))
34+
.timeout(Duration::from_secs(2))
3535
.build()
3636
.expect("Failed to build HTTP client for metrics"),
3737
}
3838
}
3939

4040
/// Fetch metrics from a node's Prometheus endpoint
4141
pub async fn fetch_node_metrics(&self, node_id: &str, endpoint: &str) -> Result<NodeMetrics, String> {
42-
let url = format!("{}/metrics", endpoint);
42+
let url = if endpoint.ends_with("/metrics") {
43+
endpoint.to_string()
44+
} else {
45+
format!("{}/metrics", endpoint)
46+
};
4347

4448
let response = self.client
4549
.get(&url)

crates/bitcell-admin/src/process.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,25 +71,32 @@ impl ProcessManager {
7171
// Build command to start node
7272
// NOTE: Uses 'cargo run' which is suitable for development only.
7373
// For production deployments, use a compiled binary path instead.
74-
let mut cmd = Command::new("cargo");
75-
cmd.arg("run")
76-
.arg("-p")
77-
.arg("bitcell-node")
78-
.arg("--")
79-
.arg(match node.config.node_type {
80-
NodeType::Validator => "validator",
81-
NodeType::Miner => "miner",
82-
NodeType::FullNode => "full-node",
83-
})
84-
.arg("--port")
85-
.arg(node.config.port.to_string())
86-
.arg("--rpc-port")
87-
.arg(node.config.rpc_port.to_string())
88-
.arg("--data-dir")
89-
.arg(&node.config.data_dir)
74+
// Build command to start node using pre-built release binary
75+
let binary_path = std::env::current_dir()
76+
.unwrap()
77+
.join("target/release/bitcell-node");
78+
79+
let mut cmd = Command::new(binary_path);
80+
81+
// Add node type and arguments
82+
match node.config.node_type {
83+
NodeType::Validator => {
84+
cmd.arg("validator");
85+
},
86+
NodeType::Miner => {
87+
cmd.arg("miner");
88+
},
89+
NodeType::FullNode => {
90+
cmd.arg("full-node");
91+
},
92+
}
93+
94+
cmd.arg("--port").arg(node.config.port.to_string())
95+
.arg("--rpc-port").arg(node.config.rpc_port.to_string())
96+
.arg("--data-dir").arg(&node.config.data_dir)
9097
.env("RUST_LOG", &node.config.log_level)
91-
.stdout(Stdio::piped())
92-
.stderr(Stdio::piped());
98+
.stdout(Stdio::inherit())
99+
.stderr(Stdio::inherit());
93100

94101
tracing::info!("Starting node '{}' with command: {:?}", id, cmd);
95102

0 commit comments

Comments
 (0)