Skip to content

Commit af73ea3

Browse files
CopilotSteake
andcommitted
Fix code review issues: node ID validation, grid_states ordering, and remove sample nodes
Co-authored-by: Steake <530040+Steake@users.noreply.github.com>
1 parent 9c6768b commit af73ea3

4 files changed

Lines changed: 42 additions & 80 deletions

File tree

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

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ pub struct StartNodeRequest {
3232
pub config: Option<serde_json::Value>,
3333
}
3434

35+
/// Validate node ID format (alphanumeric, hyphens, and underscores only)
36+
fn validate_node_id(id: &str) -> Result<(), (StatusCode, Json<ErrorResponse>)> {
37+
if !id.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
38+
return Err((
39+
StatusCode::BAD_REQUEST,
40+
Json(ErrorResponse {
41+
error: "Invalid node ID format".to_string(),
42+
}),
43+
));
44+
}
45+
Ok(())
46+
}
47+
3548
/// List all registered nodes
3649
pub async fn list_nodes(
3750
State(state): State<Arc<AppState>>,
@@ -47,15 +60,7 @@ pub async fn get_node(
4760
State(state): State<Arc<AppState>>,
4861
Path(id): Path<String>,
4962
) -> Result<Json<NodeResponse>, (StatusCode, Json<ErrorResponse>)> {
50-
// Validate node ID format (alphanumeric and hyphens only)
51-
if !id.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
52-
return Err((
53-
StatusCode::BAD_REQUEST,
54-
Json(ErrorResponse {
55-
error: "Invalid node ID format".to_string(),
56-
}),
57-
));
58-
}
63+
validate_node_id(&id)?;
5964

6065
match state.process.get_node(&id) {
6166
Some(node) => Ok(Json(NodeResponse { node })),
@@ -74,15 +79,7 @@ pub async fn start_node(
7479
Path(id): Path<String>,
7580
Json(req): Json<StartNodeRequest>,
7681
) -> Result<Json<NodeResponse>, (StatusCode, Json<ErrorResponse>)> {
77-
// Validate node ID format (alphanumeric and hyphens only)
78-
if !id.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
79-
return Err((
80-
StatusCode::BAD_REQUEST,
81-
Json(ErrorResponse {
82-
error: "Invalid node ID format".to_string(),
83-
}),
84-
));
85-
}
82+
validate_node_id(&id)?;
8683

8784
// Config is not supported yet
8885
if req.config.is_some() {
@@ -113,15 +110,7 @@ pub async fn stop_node(
113110
State(state): State<Arc<AppState>>,
114111
Path(id): Path<String>,
115112
) -> Result<Json<NodeResponse>, (StatusCode, Json<ErrorResponse>)> {
116-
// Validate node ID format (alphanumeric and hyphens only)
117-
if !id.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
118-
return Err((
119-
StatusCode::BAD_REQUEST,
120-
Json(ErrorResponse {
121-
error: "Invalid node ID format".to_string(),
122-
}),
123-
));
124-
}
113+
validate_node_id(&id)?;
125114

126115
match state.process.stop_node(&id) {
127116
Ok(node) => {

crates/bitcell-admin/src/main.rs

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! BitCell Admin Console - Main Entry Point
22
3-
use bitcell_admin::{AdminConsole, process::{ProcessManager, NodeConfig}, api::NodeType};
3+
use bitcell_admin::AdminConsole;
44
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
55

66
#[tokio::main]
@@ -23,47 +23,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
2323
.parse()?;
2424

2525
let console = AdminConsole::new(addr);
26-
let process_mgr = console.process_manager();
2726

28-
// Register some sample nodes for demonstration
29-
register_sample_nodes(&process_mgr);
30-
31-
tracing::info!("Admin console ready - registered {} nodes", process_mgr.list_nodes().len());
27+
tracing::info!("Admin console ready");
3228
tracing::info!("Dashboard available at http://{}", addr);
3329

3430
console.serve().await?;
3531

3632
Ok(())
3733
}
38-
39-
fn register_sample_nodes(process: &ProcessManager) {
40-
// Register sample validator nodes
41-
for i in 1..=3 {
42-
let config = NodeConfig {
43-
node_type: NodeType::Validator,
44-
data_dir: format!("/tmp/bitcell/validator-{}", i),
45-
port: 9000 + i as u16,
46-
rpc_port: 10000 + i as u16,
47-
log_level: "info".to_string(),
48-
network: "testnet".to_string(),
49-
};
50-
51-
process.register_node(format!("validator-{}", i), config);
52-
tracing::info!("Registered validator-{}", i);
53-
}
54-
55-
// Register sample miner nodes
56-
for i in 1..=2 {
57-
let config = NodeConfig {
58-
node_type: NodeType::Miner,
59-
data_dir: format!("/tmp/bitcell/miner-{}", i),
60-
port: 9100 + i as u16,
61-
rpc_port: 10100 + i as u16,
62-
log_level: "info".to_string(),
63-
network: "testnet".to_string(),
64-
};
65-
66-
process.register_node(format!("miner-{}", i), config);
67-
tracing::info!("Registered miner-{}", i);
68-
}
69-
}

crates/bitcell-ca/src/battle.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,35 +131,41 @@ impl Battle {
131131

132132
/// Get grid states at specific steps for visualization.
133133
///
134-
/// Returns a vector of grids at the requested step intervals.
134+
/// Returns a vector of grids at the requested step intervals in the same order
135+
/// as the input `sample_steps` array.
135136
/// Steps that exceed `self.steps` are silently skipped.
136-
/// For efficiency, steps should be provided in ascending order.
137137
///
138138
/// # Performance Note
139-
/// This implementation evolves incrementally from one step to the next,
140-
/// which is more efficient than evolving from scratch for each step.
139+
/// This implementation sorts steps internally for incremental evolution efficiency,
140+
/// but returns grids in the original order requested.
141141
pub fn grid_states(&self, sample_steps: &[usize]) -> Vec<Grid> {
142-
let mut grids = Vec::new();
143142
let initial = self.setup_grid();
144143

145-
// Sort sample_steps to ensure incremental evolution
146-
let mut sorted_steps: Vec<usize> = sample_steps.iter()
147-
.filter(|&&step| step <= self.steps)
148-
.copied()
144+
// Filter and create (index, step) pairs to preserve original order
145+
let mut indexed_steps: Vec<(usize, usize)> = sample_steps.iter()
146+
.enumerate()
147+
.filter(|(_, &step)| step <= self.steps)
148+
.map(|(idx, &step)| (idx, step))
149149
.collect();
150-
sorted_steps.sort_unstable();
151150

151+
// Sort by step for efficient incremental evolution
152+
indexed_steps.sort_unstable_by_key(|(_, step)| *step);
153+
154+
// Evolve grids in sorted order
155+
let mut evolved_grids = Vec::with_capacity(indexed_steps.len());
152156
let mut current_grid = initial;
153157
let mut prev_step = 0;
154158

155-
for step in sorted_steps {
159+
for (original_idx, step) in &indexed_steps {
156160
let steps_to_evolve = step - prev_step;
157161
current_grid = evolve_n_steps(&current_grid, steps_to_evolve);
158-
grids.push(current_grid.clone());
159-
prev_step = step;
162+
evolved_grids.push((*original_idx, current_grid.clone()));
163+
prev_step = *step;
160164
}
161165

162-
grids
166+
// Sort back to original order and extract grids
167+
evolved_grids.sort_unstable_by_key(|(idx, _)| *idx);
168+
evolved_grids.into_iter().map(|(_, grid)| grid).collect()
163169
}
164170
}
165171

crates/bitcell-ca/src/grid.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ impl Grid {
156156
///
157157
/// # Note
158158
/// When `GRID_SIZE` is not evenly divisible by `target_size`, some cells near
159-
/// the edges may not be sampled. This is acceptable for visualization purposes.
159+
/// the edges may not be sampled. For example, with `GRID_SIZE=1024` and
160+
/// `target_size=100`, `block_size=10`, so only cells from indices 0-999 are
161+
/// sampled, leaving rows/columns 1000-1023 unsampled. This is acceptable for
162+
/// visualization purposes where approximate representation is sufficient.
160163
pub fn downsample(&self, target_size: usize) -> Vec<Vec<u8>> {
161164
if target_size == 0 || target_size > GRID_SIZE {
162165
panic!("target_size must be between 1 and {}", GRID_SIZE);

0 commit comments

Comments
 (0)