Skip to content

Commit 5012d67

Browse files
committed
Address PR feedback: fix compilation errors and documentation
Changes: - Fix documentation inconsistencies: Update FINAL_REPORT.md to consistently use 92-95% completion status - Fix benchmark compilation: Update ca_benchmarks.rs to use new Grid API (Grid::new(), Position-based setters, evolve_grid function) - Fix error handling in storage.rs: Replace private rocksdb::Error::new() calls with String-based error handling
1 parent 33518e4 commit 5012d67

3 files changed

Lines changed: 809 additions & 0 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
2+
use bitcell_ca::{Grid, Glider, GliderPattern, Battle, Position, Cell};
3+
use bitcell_ca::rules::evolve_grid;
4+
5+
fn grid_creation_benchmark(c: &mut Criterion) {
6+
c.bench_function("grid_1024x1024_creation", |b| {
7+
b.iter(|| Grid::new())
8+
});
9+
}
10+
11+
fn grid_evolution_benchmark(c: &mut Criterion) {
12+
let mut grid = Grid::new();
13+
// Add some initial patterns
14+
grid.set(Position::new(100, 100), Cell::alive(128));
15+
grid.set(Position::new(100, 101), Cell::alive(128));
16+
grid.set(Position::new(101, 100), Cell::alive(128));
17+
18+
c.bench_function("grid_evolution_step", |b| {
19+
b.iter(|| {
20+
let g = grid.clone();
21+
black_box(evolve_grid(&g))
22+
});
23+
});
24+
}
25+
26+
fn glider_creation_benchmark(c: &mut Criterion) {
27+
let mut group = c.benchmark_group("glider_creation");
28+
29+
let patterns = vec![
30+
("Standard", GliderPattern::Standard),
31+
("Lightweight", GliderPattern::Lightweight),
32+
("Middleweight", GliderPattern::Middleweight),
33+
("Heavyweight", GliderPattern::Heavyweight),
34+
];
35+
36+
for (name, pattern) in patterns {
37+
group.bench_with_input(BenchmarkId::from_parameter(name), &pattern, |b, pattern| {
38+
b.iter(|| {
39+
let glider = Glider::new(*pattern, Position::new(100, 100));
40+
let mut grid = Grid::new();
41+
grid.set_pattern(glider.position, &glider.cells());
42+
black_box(grid)
43+
});
44+
});
45+
}
46+
group.finish();
47+
}
48+
49+
fn battle_simulation_benchmark(c: &mut Criterion) {
50+
c.bench_function("battle_simulation", |b| {
51+
let glider_a = Glider::new(GliderPattern::Heavyweight, Position::new(200, 200));
52+
let glider_b = Glider::new(GliderPattern::Standard, Position::new(800, 800));
53+
let battle = Battle::new(glider_a, glider_b);
54+
55+
b.iter(|| {
56+
let b = battle.clone();
57+
black_box(b.simulate())
58+
});
59+
});
60+
}
61+
62+
fn parallel_grid_evolution_benchmark(c: &mut Criterion) {
63+
let mut grid = Grid::new();
64+
// Add scattered patterns for realistic parallel workload
65+
for i in 0..10 {
66+
for j in 0..10 {
67+
grid.set(Position::new(i * 100, j * 100), Cell::alive(200));
68+
}
69+
}
70+
71+
c.bench_function("parallel_evolution_step", |b| {
72+
b.iter(|| {
73+
let g = grid.clone();
74+
black_box(evolve_grid(&g))
75+
});
76+
});
77+
}
78+
79+
criterion_group!(
80+
benches,
81+
grid_creation_benchmark,
82+
grid_evolution_benchmark,
83+
glider_creation_benchmark,
84+
battle_simulation_benchmark,
85+
parallel_grid_evolution_benchmark
86+
);
87+
criterion_main!(benches);
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/// RocksDB persistent storage layer
2+
/// Provides durable storage for blocks, state, and chain data
3+
4+
use rocksdb::{DB, Options, WriteBatch, IteratorMode};
5+
use std::path::Path;
6+
use std::sync::Arc;
7+
use serde::{Serialize, Deserialize};
8+
9+
use crate::{Account, BondState};
10+
11+
/// Database column families
12+
const CF_BLOCKS: &str = "blocks";
13+
const CF_HEADERS: &str = "headers";
14+
const CF_TRANSACTIONS: &str = "transactions";
15+
const CF_ACCOUNTS: &str = "accounts";
16+
const CF_BONDS: &str = "bonds";
17+
const CF_STATE_ROOTS: &str = "state_roots";
18+
const CF_CHAIN_INDEX: &str = "chain_index";
19+
20+
/// Persistent storage manager
21+
pub struct StorageManager {
22+
db: Arc<DB>,
23+
}
24+
25+
impl StorageManager {
26+
/// Open or create a database
27+
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, rocksdb::Error> {
28+
let mut opts = Options::default();
29+
opts.create_if_missing(true);
30+
opts.create_missing_column_families(true);
31+
32+
let cfs = vec![
33+
CF_BLOCKS,
34+
CF_HEADERS,
35+
CF_TRANSACTIONS,
36+
CF_ACCOUNTS,
37+
CF_BONDS,
38+
CF_STATE_ROOTS,
39+
CF_CHAIN_INDEX,
40+
];
41+
42+
let db = DB::open_cf(&opts, path, cfs)?;
43+
44+
Ok(Self {
45+
db: Arc::new(db),
46+
})
47+
}
48+
49+
/// Store a block header
50+
pub fn store_header(&self, height: u64, hash: &[u8], header: &[u8]) -> Result<(), String> {
51+
let cf = self.db.cf_handle(CF_HEADERS)
52+
.ok_or_else(|| "Headers column family not found".to_string())?;
53+
54+
let mut batch = WriteBatch::default();
55+
// Store by height
56+
batch.put_cf(cf, height.to_be_bytes(), header);
57+
// Store by hash
58+
batch.put_cf(cf, hash, header);
59+
// Update chain index
60+
let index_cf = self.db.cf_handle(CF_CHAIN_INDEX)
61+
.ok_or_else(|| "Chain index column family not found".to_string())?;
62+
batch.put_cf(index_cf, b"latest_height", height.to_be_bytes());
63+
batch.put_cf(index_cf, b"latest_hash", hash);
64+
65+
self.db.write(batch).map_err(|e| e.to_string())
66+
}
67+
68+
/// Store a full block
69+
pub fn store_block(&self, hash: &[u8], block: &[u8]) -> Result<(), String> {
70+
let cf = self.db.cf_handle(CF_BLOCKS)
71+
.ok_or_else(|| "Blocks column family not found".to_string())?;
72+
self.db.put_cf(cf, hash, block).map_err(|e| e.to_string())
73+
}
74+
75+
/// Get block by hash
76+
pub fn get_block(&self, hash: &[u8]) -> Result<Option<Vec<u8>>, String> {
77+
let cf = self.db.cf_handle(CF_BLOCKS)
78+
.ok_or_else(|| "Blocks column family not found".to_string())?;
79+
self.db.get_cf(cf, hash).map_err(|e| e.to_string())
80+
}
81+
82+
/// Get header by height
83+
pub fn get_header_by_height(&self, height: u64) -> Result<Option<Vec<u8>>, String> {
84+
let cf = self.db.cf_handle(CF_HEADERS)
85+
.ok_or_else(|| "Headers column family not found".to_string())?;
86+
self.db.get_cf(cf, height.to_be_bytes()).map_err(|e| e.to_string())
87+
}
88+
89+
/// Get header by hash
90+
pub fn get_header_by_hash(&self, hash: &[u8]) -> Result<Option<Vec<u8>>, String> {
91+
let cf = self.db.cf_handle(CF_HEADERS)
92+
.ok_or_else(|| "Headers column family not found".to_string())?;
93+
self.db.get_cf(cf, hash).map_err(|e| e.to_string())
94+
}
95+
96+
/// Get latest chain height
97+
pub fn get_latest_height(&self) -> Result<Option<u64>, String> {
98+
let cf = self.db.cf_handle(CF_CHAIN_INDEX)
99+
.ok_or_else(|| "Chain index column family not found".to_string())?;
100+
if let Some(bytes) = self.db.get_cf(cf, b"latest_height").map_err(|e| e.to_string())? {
101+
let height = u64::from_be_bytes(
102+
bytes.as_slice().try_into()
103+
.map_err(|_| "Invalid height data".to_string())?
104+
);
105+
Ok(Some(height))
106+
} else {
107+
Ok(None)
108+
}
109+
}
110+
111+
/// Store account state
112+
pub fn store_account(&self, address: &[u8], account: &Account) -> Result<(), String> {
113+
let cf = self.db.cf_handle(CF_ACCOUNTS)
114+
.ok_or_else(|| "Accounts column family not found".to_string())?;
115+
let data = bincode::serialize(account)
116+
.map_err(|e| format!("Serialization error: {}", e))?;
117+
self.db.put_cf(cf, address, data).map_err(|e| e.to_string())
118+
}
119+
120+
/// Get account state
121+
pub fn get_account(&self, address: &[u8]) -> Result<Option<Account>, String> {
122+
let cf = self.db.cf_handle(CF_ACCOUNTS)
123+
.ok_or_else(|| "Accounts column family not found".to_string())?;
124+
if let Some(data) = self.db.get_cf(cf, address).map_err(|e| e.to_string())? {
125+
Ok(bincode::deserialize(&data).ok())
126+
} else {
127+
Ok(None)
128+
}
129+
}
130+
131+
/// Store bond state
132+
pub fn store_bond(&self, miner_id: &[u8], bond: &BondState) -> Result<(), String> {
133+
let cf = self.db.cf_handle(CF_BONDS)
134+
.ok_or_else(|| "Bonds column family not found".to_string())?;
135+
let data = bincode::serialize(bond)
136+
.map_err(|e| format!("Serialization error: {}", e))?;
137+
self.db.put_cf(cf, miner_id, data).map_err(|e| e.to_string())
138+
}
139+
140+
/// Get bond state
141+
pub fn get_bond(&self, miner_id: &[u8]) -> Result<Option<BondState>, String> {
142+
let cf = self.db.cf_handle(CF_BONDS)
143+
.ok_or_else(|| "Bonds column family not found".to_string())?;
144+
if let Some(data) = self.db.get_cf(cf, miner_id).map_err(|e| e.to_string())? {
145+
Ok(bincode::deserialize(&data).ok())
146+
} else {
147+
Ok(None)
148+
}
149+
}
150+
151+
/// Store state root for a given height
152+
pub fn store_state_root(&self, height: u64, root: &[u8]) -> Result<(), String> {
153+
let cf = self.db.cf_handle(CF_STATE_ROOTS)
154+
.ok_or_else(|| "State roots column family not found".to_string())?;
155+
self.db.put_cf(cf, height.to_be_bytes(), root).map_err(|e| e.to_string())
156+
}
157+
158+
/// Get state root for a given height
159+
pub fn get_state_root(&self, height: u64) -> Result<Option<Vec<u8>>, String> {
160+
let cf = self.db.cf_handle(CF_STATE_ROOTS)
161+
.ok_or_else(|| "State roots column family not found".to_string())?;
162+
self.db.get_cf(cf, height.to_be_bytes()).map_err(|e| e.to_string())
163+
}
164+
165+
/// Prune old blocks (keep last N blocks)
166+
pub fn prune_old_blocks(&self, keep_last: u64) -> Result<(), String> {
167+
let latest = self.get_latest_height()?.unwrap_or(0);
168+
if latest <= keep_last {
169+
return Ok(());
170+
}
171+
172+
let prune_until = latest - keep_last;
173+
let _cf = self.db.cf_handle(CF_BLOCKS)
174+
.ok_or_else(|| "Blocks column family not found".to_string())?;
175+
176+
// This is a simplified version - in production would iterate and delete
177+
for height in 0..prune_until {
178+
if let Some(header_data) = self.get_header_by_height(height)? {
179+
// Extract hash and delete block
180+
// (Simplified - would need proper header deserialization)
181+
let _ = header_data;
182+
}
183+
}
184+
185+
Ok(())
186+
}
187+
188+
/// Get database statistics
189+
pub fn get_stats(&self) -> Result<String, rocksdb::Error> {
190+
self.db.property_value("rocksdb.stats")
191+
.map(|v| v.unwrap_or_else(|| "No stats available".to_string()))
192+
}
193+
}
194+
195+
#[cfg(test)]
196+
mod tests {
197+
use super::*;
198+
use tempfile::TempDir;
199+
200+
#[test]
201+
fn test_storage_manager_creation() {
202+
let temp_dir = TempDir::new().unwrap();
203+
let result = StorageManager::new(temp_dir.path());
204+
assert!(result.is_ok());
205+
}
206+
207+
#[test]
208+
fn test_store_and_retrieve_header() {
209+
let temp_dir = TempDir::new().unwrap();
210+
let storage = StorageManager::new(temp_dir.path()).unwrap();
211+
212+
let height = 100u64;
213+
let hash = b"test_hash_12345678";
214+
let header = b"test_header_data";
215+
216+
storage.store_header(height, hash, header).unwrap();
217+
218+
let retrieved = storage.get_header_by_height(height).unwrap();
219+
assert_eq!(retrieved.as_deref(), Some(header.as_slice()));
220+
221+
let by_hash = storage.get_header_by_hash(hash).unwrap();
222+
assert_eq!(by_hash.as_deref(), Some(header.as_slice()));
223+
}
224+
225+
#[test]
226+
fn test_latest_height() {
227+
let temp_dir = TempDir::new().unwrap();
228+
let storage = StorageManager::new(temp_dir.path()).unwrap();
229+
230+
assert_eq!(storage.get_latest_height().unwrap(), None);
231+
232+
storage.store_header(42, b"hash", b"header").unwrap();
233+
assert_eq!(storage.get_latest_height().unwrap(), Some(42));
234+
}
235+
}

0 commit comments

Comments
 (0)