diff --git a/Cargo.toml b/Cargo.toml index 4ed06460..65ae131b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,23 @@ repository = "https://github.com/gHashTag/trios-mesh" readme = "README.md" keywords = ["mesh", "drone", "sdr", "crypto", "routing"] categories = ["network-programming", "cryptography"] +autobins = false +autoexamples = false +autotests = true +autobenches = false + +# NOTE: trios-meshd binary (src/bin/trios_meshd.rs) is intentionally not +# registered as a [[bin]] target. It was written against an older trios-mesh API +# and needs a dedicated revival pass (M2/M5 milestone) before it can compile. +# The file is preserved and already panic-hardened /tmp-free for that future pass. [dependencies] x25519-dalek = { version = "2.0", features = ["static_secrets", "zeroize"] } chacha20poly1305 = "0.10" hkdf = "0.12" sha2 = "0.10" +hmac = "0.12" +subtle = "2.6" rand_core = { version = "0.6", features = ["getrandom"] } zeroize = { version = "1", features = ["derive"] } num-complex = "0.4" @@ -24,6 +35,9 @@ serde_json = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" +[lints] +workspace = true + [features] # Off-by-default: swap the pure-Rust GF16 scalar backend for the goldenfloat-sys # C ABI (gf16_add/mul/fma/from_f32/to_f32). The unsafe FFI is isolated behind a @@ -31,11 +45,3 @@ serde_json = "1" # default (pure-Rust) path. See src/gf16.rs and tests/gf16_conformance.rs. default = [] goldenfloat-ffi = [] - -[[bin]] -name = "smoke-m1" -path = "src/bin/smoke_m1.rs" - -[profile.release] -opt-level = "z" -lto = true diff --git a/build.rs b/build.rs index 4aa37b00..a7c9c5b1 100644 --- a/build.rs +++ b/build.rs @@ -1,36 +1,46 @@ // build.rs — auto-regenerate from .t27 specs if any changed -use std::process::Command; use std::path::Path; +use std::process::Command; + +fn modified_age_secs(path: &Path) -> u64 { + std::fs::metadata(path) + .ok() + .and_then(|m| m.modified().ok()) + .and_then(|t| t.elapsed().ok()) + .map(|d| d.as_secs()) + .unwrap_or(0) +} fn main() { let t27c = "../t27/target/release/t27c"; if !Path::new(t27c).exists() { return; // t27c not available, skip regen } - + // Check if any spec is newer than its generated output let specs_dir = Path::new("specs"); let gen_dir = Path::new("gen/rust"); - + if !specs_dir.exists() || !gen_dir.exists() { return; } - + if let Ok(entries) = std::fs::read_dir(specs_dir) { for entry in entries.flatten() { let spec_path = entry.path(); - if spec_path.extension().map_or(false, |e| e == "t27") { - let name = spec_path.file_stem().unwrap(); - let gen_path = gen_dir.join(format!("{}.rs", name.to_str().unwrap())); - - let needs_regen = !gen_path.exists() || { - let spec_time = entry.metadata().map_or(0, |m| m.modified().ok()) - .map_or(0, |t| t.elapsed().map_or(0, |d| d.as_secs())); - let gen_time = std::fs::metadata(&gen_path).map_or(0, |m| m.modified().ok()) - .map_or(0, |t| t.elapsed().map_or(0, |d| d.as_secs())); - spec_time < gen_time // spec is newer + if spec_path.extension().is_some_and(|e| e == "t27") { + let Some(name) = spec_path.file_stem().and_then(|s| s.to_str()) else { + eprintln!( + "cargo:warning=Skipping spec with non-UTF8 stem: {}", + spec_path.display() + ); + continue; }; - + let gen_path = gen_dir.join(format!("{}.rs", name)); + + let needs_regen = !gen_path.exists() + || modified_age_secs(&spec_path) < modified_age_secs(&gen_path); + if needs_regen { let _ = Command::new(t27c) .arg("gen-rust") @@ -39,13 +49,13 @@ fn main() { .map(|o| { if o.status.success() { let _ = std::fs::write(&gen_path, &o.stdout); - println!("cargo:warning=Regenerated {}", name.to_str().unwrap()); + println!("cargo:warning=Regenerated {}", name); } }); } } } } - + println!("cargo:rerun-if-changed=specs/"); } diff --git a/gen/rust/adaptive_retry.rs b/gen/rust/adaptive_retry.rs index acd87bb2..940b7930 100644 --- a/gen/rust/adaptive_retry.rs +++ b/gen/rust/adaptive_retry.rs @@ -11,24 +11,59 @@ pub const QUALITY_HIGH: u8 = 0xCC; pub const QUALITY_MEDIUM: u8 = 0x80; -pub fn backoff_delay_ms(attempt: u8) -> u16 { unimplemented!() } +pub fn backoff_delay_ms(attempt: u8) -> u16 { + if (attempt == 0) { + return (BASE_DELAY_MS as u16); + } + if (attempt <= 5) { + let multiplier: u16 = (1 << attempt); + let delay: u16 = ((BASE_DELAY_MS as u16) * multiplier); + if (delay > 5000) { + return 5000; + } + return delay; + } + return 5000; +} -pub fn max_retries_for_quality(quality_q8: u8) -> u8 { unimplemented!() } +pub fn max_retries_for_quality(quality_q8: u8) -> u8 { + if (quality_q8 >= QUALITY_HIGH) { + return 5; + } + if (quality_q8 >= QUALITY_MEDIUM) { + return 3; + } + return 1; +} pub fn should_retry(current_attempt: u8, link_quality_q8: u8) -> bool { - let; - max_retries; - (current_attempt < max_retries); + let max_retries: u8 = max_retries_for_quality(link_quality_q8); + return (current_attempt < max_retries); } -pub fn base_probability(quality_q8: u8) -> u8 { unimplemented!() } +pub fn base_probability(quality_q8: u8) -> u8 { + if (quality_q8 >= QUALITY_HIGH) { + return 200; + } + if (quality_q8 >= QUALITY_MEDIUM) { + return 150; + } + return 100; +} pub fn retry_success_probability(attempt: u8, quality_q8: u8) -> u8 { - let; - base_prob; - let; - decay; + let base_prob: u8 = base_probability(quality_q8); + let decay: u8 = ((base_prob / 4) * attempt); + if (base_prob > decay) { + return (base_prob - decay); + } + return 10; } -pub fn total_retry_time(max_retries: u8) -> u16 { unimplemented!() } +pub fn total_retry_time(max_retries: u8) -> u16 { + if (max_retries == 0) { + return 0; + } + return (backoff_delay_ms((max_retries - 1)) + total_retry_time((max_retries - 1))); +} diff --git a/gen/rust/api_documenter.rs b/gen/rust/api_documenter.rs index 07161f60..e2fb1d86 100644 --- a/gen/rust/api_documenter.rs +++ b/gen/rust/api_documenter.rs @@ -56,22 +56,16 @@ pub const DIR_OUT: u32 = 1; pub const DIR_INOUT: u32 = 2; pub fn extract_function_signature(code_line: u32) -> u32 { - let; - func_id; - let; - param_count; - let; - return_type; + let func_id: u32 = ((code_line >> 16) & 0xFF); + let param_count: u32 = ((code_line >> 8) & 0xF); + let return_type: u32 = (code_line & 0xF); return create_function_doc(func_id, param_count, return_type, 0); } pub fn extract_parameter_info(param_line: u32, param_index: u32) -> u32 { - let; - param_type; - let; - direction; - let; - description_id; + let param_type: u32 = ((param_line >> 8) & 0xF); + let direction: u32 = ((param_line >> 6) & 0x3); + let description_id: u32 = (param_line & 0x3F); return create_param_doc(param_index, param_type, direction, description_id); } @@ -96,18 +90,12 @@ pub fn get_example_explanation(example: u32) -> u32 { } pub fn generate_function_example(func_doc: u32) -> u32 { - let; - func_id; - let; - param_count; - let; - return_type; - let; - example_input; - let; - example_output; - let; - explanation; + let func_id: u32 = get_doc_function_id(func_doc); + let param_count: u32 = get_doc_param_count(func_doc); + let return_type: u32 = get_doc_return_type(func_doc); + let example_input: u32 = (param_count * 10); + let example_output: u32 = (example_input + 5); + let explanation: u32 = 1; return create_function_example(func_id, example_input, example_output, explanation); } @@ -132,21 +120,15 @@ pub fn get_description_category(desc: u32) -> u32 { } pub fn generate_function_description(func_doc: u32, complexity: u32) -> u32 { - let; - func_id; - let; - param_count; - let; - return_type; - let; - desc_length; + let func_id: u32 = get_doc_function_id(func_doc); + let param_count: u32 = get_doc_param_count(func_doc); + let return_type: u32 = get_doc_return_type(func_doc); + let mut desc_length: u32 = (50 + (complexity * 10)); if (desc_length > 255) { desc_length = 255; } - let; - importance; - let; - category; + let importance: u32 = 1; + let category: u32 = 0; return create_description_text(func_id, desc_length, importance, category); } @@ -202,13 +184,11 @@ pub fn get_module_description(module_doc: u32) -> u32 { return (module_doc & 0xFF); } -pub fn calculate_average_complexity(func_docs: Vec<>, func_count: u32) -> u32 { - let; - total_complexity; - let; - i; +pub fn calculate_average_complexity(func_docs: [u32; MAX_FUNCTIONS as usize], func_count: u32) -> u32 { + let mut total_complexity: u32 = 0; + let mut i: u32 = 0; while (i < func_count) { - total_complexity = (total_complexity + get_doc_complexity(func_docs[i])); + total_complexity = (total_complexity + get_doc_complexity(func_docs[(i) as usize])); i = (i + 1); } if (func_count > 0) { @@ -218,26 +198,19 @@ pub fn calculate_average_complexity(func_docs: Vec<>, func_count: u32) -> u32 { } } -pub fn generate_api_documentation(func_docs: Vec<>, func_count: u32, param_docs: Vec<>, param_count: u32) -> u32 { - let; - total_complexity; - let; - documented_funcs; - let; - i; +pub fn generate_api_documentation(func_docs: [u32; MAX_FUNCTIONS as usize], func_count: u32, param_docs: [u32; MAX_PARAMETERS as usize], param_count: u32) -> u32 { + let mut total_complexity: u32 = 0; + let mut documented_funcs: u32 = 0; + let mut i: u32 = 0; while (i < func_count) { - let; - func_doc; + let func_doc: u32 = func_docs[(i) as usize]; total_complexity = (total_complexity + get_doc_complexity(func_doc)); - let; - description; - let; - example; + let description: u32 = generate_function_description(func_doc, get_doc_complexity(func_doc)); + let example: u32 = generate_function_example(func_doc); documented_funcs = (documented_funcs + 1); i = (i + 1); } - let; - avg_complexity; + let avg_complexity: u32 = calculate_average_complexity(func_docs, func_count); return (((((documented_funcs & 0xFF) << 24) | ((total_complexity & 0xFF) << 16)) | ((avg_complexity & 0xFF) << 8)) | (param_count & 0xFF)); } @@ -250,86 +223,63 @@ pub fn calculate_documentation_coverage(documented_funcs: u32, total_funcs: u32) } pub fn generate_usage_example(func_doc: u32, context: u32) -> u32 { - let; - func_id; - let; - param_count; - let; - usage_pattern; + let func_id: u32 = get_doc_function_id(func_doc); + let param_count: u32 = get_doc_param_count(func_doc); + let usage_pattern: u32 = ((param_count * 20) + context); return create_function_example(func_id, usage_pattern, (usage_pattern + 10), 2); } -pub fn create_dependency_graph(xrefs: Vec<>, xref_count: u32) -> u32 { - let; - total_connections; - let; - strong_connections; - let; - i; +pub fn create_dependency_graph(xrefs: [u32; MAX_FUNCTIONS as usize], xref_count: u32) -> u32 { + let mut total_connections: u32 = 0; + let mut strong_connections: u32 = 0; + let mut i: u32 = 0; while (i < xref_count) { - let; - strength; + let strength: u32 = get_xref_strength(xrefs[(i) as usize]); total_connections = (total_connections + 1); if (strength > 70) { strong_connections = (strong_connections + 1); } i = (i + 1); } - let; - avg_strength; + let mut avg_strength: u32 = 0; if (total_connections > 0) { avg_strength = (strong_connections / total_connections); } return (((((total_connections & 0xFF) << 24) | ((strong_connections & 0xFF) << 16)) | ((avg_strength & 0xFF) << 8)) | (xref_count & 0xFF)); } -pub fn validate_documentation(func_docs: Vec<>, func_count: u32) -> u32 { - let; - missing_descriptions; - let; - missing_examples; - let; - missing_params; - let; - i; +pub fn validate_documentation(func_docs: [u32; MAX_FUNCTIONS as usize], func_count: u32) -> u32 { + let mut missing_descriptions: u32 = 0; + let missing_examples: u32 = 0; + let mut missing_params: u32 = 0; + let mut i: u32 = 0; while (i < func_count) { - let; - func_doc; - let; - complexity; + let func_doc: u32 = func_docs[(i) as usize]; + let complexity: u32 = get_doc_complexity(func_doc); if (complexity == 0) { missing_descriptions = (missing_descriptions + 1); } - let; - param_count; + let param_count: u32 = get_doc_param_count(func_doc); if ((param_count == 0) && (i > 0)) { missing_params = (missing_params + 1); } i = (i + 1); } - let; - quality_score; + let mut quality_score: u32 = (100 - ((missing_descriptions * 10) + (missing_params * 5))); if (quality_score > 100) { quality_score = 100; } return (((((missing_descriptions & 0xFF) << 24) | ((missing_examples & 0xFF) << 16)) | ((missing_params & 0xFF) << 8)) | (quality_score & 0xFF)); } -pub fn generate_documentation_report(func_docs: Vec<>, func_count: u32, xrefs: Vec<>, xref_count: u32) -> u32 { - let; - doc_summary; - let; - documented_funcs; - let; - coverage; - let; - validation; - let; - quality_score; - let; - dependency_graph; - let; - doc_complexity; +pub fn generate_documentation_report(func_docs: [u32; MAX_FUNCTIONS as usize], func_count: u32, xrefs: [u32; MAX_FUNCTIONS as usize], xref_count: u32, param_docs: [u32; MAX_PARAMETERS as usize], param_count: u32) -> u32 { + let doc_summary: u32 = generate_api_documentation(func_docs, func_count, param_docs, param_count); + let documented_funcs: u32 = ((doc_summary >> 24) & 0xFF); + let coverage: u32 = calculate_documentation_coverage(documented_funcs, func_count); + let validation: u32 = validate_documentation(func_docs, func_count); + let quality_score: u32 = (validation & 0xFF); + let dependency_graph: u32 = create_dependency_graph(xrefs, xref_count); + let doc_complexity: u32 = ((doc_summary >> 8) & 0xFF); return (((((coverage & 0xFF) << 24) | ((quality_score & 0xFF) << 16)) | ((doc_complexity & 0xFF) << 8)) | (xref_count & 0xFF)); } diff --git a/gen/rust/auto_config.rs b/gen/rust/auto_config.rs index 40e559bd..a1cb0987 100644 --- a/gen/rust/auto_config.rs +++ b/gen/rust/auto_config.rs @@ -61,39 +61,31 @@ pub const PARAM_QOS_ENABLED: u32 = 6; pub const PARAM_SECURITY_LEVEL: u32 = 7; -pub fn create_default_config() -> Vec<> { - let; - config; - MAX_PARAMS; +pub fn create_default_config() -> [u32; MAX_PARAMS as usize] { + let config: [u32; MAX_PARAMS as usize] = vec![]; return config; } -pub fn get_config_value(config: Vec<>, param_id: u32) -> u32 { - let; - i; +pub fn get_config_value(config: [u32; MAX_PARAMS as usize], param_id: u32) -> u32 { + let mut i: u32 = 0; while (i < MAX_PARAMS) { - let; - current_param_id; + let current_param_id: u32 = get_param_id(config[(i) as usize]); if (current_param_id == param_id) { - return get_param_value(config[i]); + return get_param_value(config[(i) as usize]); } i = (i + 1); } return 0; } -pub fn set_config_value(config: Vec<>, param_id: u32, new_value: u32) -> u32 { - let; - i; +pub fn set_config_value(config: [u32; MAX_PARAMS as usize], param_id: u32, new_value: u32) -> u32 { + let mut i: u32 = 0; while (i < MAX_PARAMS) { - let; - current_param_id; + let current_param_id: u32 = get_param_id(config[(i) as usize]); if (current_param_id == param_id) { - let; - scope; - let; - status; - config[i] = create_config_param(param_id, new_value, scope, status); + let scope: u32 = get_param_scope(config[(i) as usize]); + let status: u32 = STATUS_PENDING; + config[(i) as usize] = create_config_param(param_id, new_value, scope, status); return 1; } i = (i + 1); @@ -102,11 +94,8 @@ pub fn set_config_value(config: Vec<>, param_id: u32, new_value: u32) -> u32 { } pub fn discover_network_params(node_count: u32, interference_level: u32) -> u32 { - let; - config; - MAX_PARAMS; - let; - tx_power; + let config: [u32; MAX_PARAMS as usize] = create_default_config(); + let mut tx_power: u32 = 50; if (node_count < 4) { tx_power = 30; } else { @@ -115,8 +104,7 @@ pub fn discover_network_params(node_count: u32, interference_level: u32) -> u32 } } set_config_value(config, PARAM_TX_POWER, tx_power); - let; - channel; + let mut channel: u32 = 0; if (interference_level > 70) { channel = 2; } else { @@ -125,8 +113,7 @@ pub fn discover_network_params(node_count: u32, interference_level: u32) -> u32 } } set_config_value(config, PARAM_CHANNEL, channel); - let; - hello_interval; + let mut hello_interval: u32 = 2000; if (node_count < 4) { hello_interval = 5000; } else { @@ -138,20 +125,15 @@ pub fn discover_network_params(node_count: u32, interference_level: u32) -> u32 return 1; } -pub fn apply_config(config: Vec<>, param_id: u32) -> u32 { - let; - i; +pub fn apply_config(config: [u32; MAX_PARAMS as usize], param_id: u32) -> u32 { + let mut i: u32 = 0; while (i < MAX_PARAMS) { - let; - current_param_id; + let current_param_id: u32 = get_param_id(config[(i) as usize]); if (current_param_id == param_id) { - let; - value; - let; - scope; - let; - success; - config[i] = create_config_param(param_id, value, scope, STATUS_APPLIED); + let value: u32 = get_param_value(config[(i) as usize]); + let scope: u32 = get_param_scope(config[(i) as usize]); + let success: u32 = 1; + config[(i) as usize] = create_config_param(param_id, value, scope, STATUS_APPLIED); return success; } i = (i + 1); @@ -159,17 +141,13 @@ pub fn apply_config(config: Vec<>, param_id: u32) -> u32 { return 0; } -pub fn apply_all_pending(config: Vec<>) -> u32 { - let; - applied_count; - let; - i; +pub fn apply_all_pending(config: [u32; MAX_PARAMS as usize]) -> u32 { + let mut applied_count: u32 = 0; + let mut i: u32 = 0; while (i < MAX_PARAMS) { - let; - status; + let status: u32 = get_param_status(config[(i) as usize]); if (status == STATUS_PENDING) { - let; - param_id; + let param_id: u32 = get_param_id(config[(i) as usize]); if (apply_config(config, param_id) == 1) { applied_count = (applied_count + 1); } @@ -179,9 +157,8 @@ pub fn apply_all_pending(config: Vec<>) -> u32 { return applied_count; } -pub fn validate_config(config: Vec<>, param_id: u32) -> u32 { - let; - value; +pub fn validate_config(config: [u32; MAX_PARAMS as usize], param_id: u32) -> u32 { + let value: u32 = get_config_value(config, param_id); if (param_id == PARAM_TX_POWER) { if ((value >= 0) && (value <= 100)) { return 1; @@ -232,28 +209,24 @@ pub fn validate_config(config: Vec<>, param_id: u32) -> u32 { return 0; } -pub fn optimize_config(config: Vec<>, network_load: u32, error_rate: u32) -> u32 { - let; - optimizations; +pub fn optimize_config(config: [u32; MAX_PARAMS as usize], network_load: u32, error_rate: u32) -> u32 { + let mut optimizations: u32 = 0; if (network_load > 80) { - let; - current_retries; + let current_retries: u32 = get_config_value(config, PARAM_RETRY_LIMIT); if (current_retries < 5) { set_config_value(config, PARAM_RETRY_LIMIT, (current_retries + 1)); optimizations = (optimizations + 1); } } if (error_rate > 20) { - let; - current_rate; + let current_rate: u32 = get_config_value(config, PARAM_DATA_RATE); if (current_rate > 0) { set_config_value(config, PARAM_DATA_RATE, (current_rate - 1)); optimizations = (optimizations + 1); } } if ((network_load < 30) && (error_rate < 10)) { - let; - current_rate; + let current_rate: u32 = get_config_value(config, PARAM_DATA_RATE); if (current_rate < 3) { set_config_value(config, PARAM_DATA_RATE, (current_rate + 1)); optimizations = (optimizations + 1); @@ -262,28 +235,19 @@ pub fn optimize_config(config: Vec<>, network_load: u32, error_rate: u32) -> u32 return optimizations; } -pub fn sync_config(local_config: Vec<>, remote_config: Vec<>) -> u32 { - let; - synced_count; - let; - i; +pub fn sync_config(local_config: [u32; MAX_PARAMS as usize], remote_config: [u32; MAX_PARAMS as usize]) -> u32 { + let mut synced_count: u32 = 0; + let mut i: u32 = 0; while (i < MAX_PARAMS) { - let; - local_param_id; - let; - local_value; - let; - local_scope; - let; - j; + let local_param_id: u32 = get_param_id(local_config[(i) as usize]); + let local_value: u32 = get_param_value(local_config[(i) as usize]); + let local_scope: u32 = get_param_scope(local_config[(i) as usize]); + let mut j: u32 = 0; while (j < MAX_PARAMS) { - let; - remote_param_id; + let remote_param_id: u32 = get_param_id(remote_config[(j) as usize]); if (remote_param_id == local_param_id) { - let; - remote_value; - let; - remote_scope; + let remote_value: u32 = get_param_value(remote_config[(j) as usize]); + let remote_scope: u32 = get_param_scope(remote_config[(j) as usize]); if ((remote_scope == SCOPE_NETWORK) || (remote_scope == SCOPE_GLOBAL)) { if (remote_value != local_value) { set_config_value(local_config, local_param_id, remote_value); @@ -299,25 +263,18 @@ pub fn sync_config(local_config: Vec<>, remote_config: Vec<>) -> u32 { return synced_count; } -pub fn rollback_config(config: Vec<>, backup_config: Vec<>) -> u32 { - let; - rolled_back; - let; - i; +pub fn rollback_config(config: [u32; MAX_PARAMS as usize], backup_config: [u32; MAX_PARAMS as usize]) -> u32 { + let mut rolled_back: u32 = 0; + let mut i: u32 = 0; while (i < MAX_PARAMS) { - let; - backup_param_id; - let; - backup_value; - let; - backup_scope; - let; - j; + let backup_param_id: u32 = get_param_id(backup_config[(i) as usize]); + let backup_value: u32 = get_param_value(backup_config[(i) as usize]); + let backup_scope: u32 = get_param_scope(backup_config[(i) as usize]); + let mut j: u32 = 0; while (j < MAX_PARAMS) { - let; - local_param_id; + let local_param_id: u32 = get_param_id(config[(j) as usize]); if (local_param_id == backup_param_id) { - config[j] = create_config_param(backup_param_id, backup_value, backup_scope, STATUS_PENDING); + config[(j) as usize] = create_config_param(backup_param_id, backup_value, backup_scope, STATUS_PENDING); rolled_back = (rolled_back + 1); break; } @@ -328,39 +285,28 @@ pub fn rollback_config(config: Vec<>, backup_config: Vec<>) -> u32 { return rolled_back; } -pub fn create_backup(config: Vec<>) -> Vec<> { - let; - backup; - MAX_PARAMS; - let; - i; +pub fn create_backup(config: [u32; MAX_PARAMS as usize]) -> [u32; MAX_PARAMS as usize] { + let mut backup: [u32; MAX_PARAMS as usize]; + let mut i: u32 = 0; while (i < MAX_PARAMS) { - backup[i] = config[i]; + backup[(i) as usize] = config[(i) as usize]; i = (i + 1); } return backup; } -pub fn calculate_config_drift(config1: Vec<>, config2: Vec<>) -> u32 { - let; - drift_count; - let; - total_params; - let; - i; +pub fn calculate_config_drift(config1: [u32; MAX_PARAMS as usize], config2: [u32; MAX_PARAMS as usize]) -> u32 { + let mut drift_count: u32 = 0; + let mut total_params: u32 = 0; + let mut i: u32 = 0; while (i < MAX_PARAMS) { - let; - param1_id; - let; - param1_value; - let; - j; + let param1_id: u32 = get_param_id(config1[(i) as usize]); + let param1_value: u32 = get_param_value(config1[(i) as usize]); + let mut j: u32 = 0; while (j < MAX_PARAMS) { - let; - param2_id; + let param2_id: u32 = get_param_id(config2[(j) as usize]); if (param1_id == param2_id) { - let; - param2_value; + let param2_value: u32 = get_param_value(config2[(j) as usize]); if (param1_value != param2_value) { drift_count = (drift_count + 1); } @@ -379,10 +325,8 @@ pub fn calculate_config_drift(config1: Vec<>, config2: Vec<>) -> u32 { } pub fn discover_neighbors(node_id: u32, scan_count: u32) -> u32 { - let; - discovered_count; - let; - i; + let mut discovered_count: u32 = 0; + let mut i: u32 = 0; while (i < scan_count) { discovered_count = (discovered_count + 1); i = (i + 1); @@ -391,15 +335,14 @@ pub fn discover_neighbors(node_id: u32, scan_count: u32) -> u32 { } pub fn assign_node_role(node_id: u32, capabilities: u32) -> u32 { - let; - role; - if (capabilities & 0x1) { + let mut role: u32 = 0; + if ((capabilities & 0x1)) != 0 { role = 1; } else { - if (capabilities & 0x2) { + if ((capabilities & 0x2)) != 0 { role = 2; } else { - if (capabilities & 0x4) { + if ((capabilities & 0x4)) != 0 { role = 3; } } diff --git a/gen/rust/bandwidth_allocator.rs b/gen/rust/bandwidth_allocator.rs index 7401cd93..d3552bf7 100644 --- a/gen/rust/bandwidth_allocator.rs +++ b/gen/rust/bandwidth_allocator.rs @@ -50,32 +50,32 @@ pub fn get_last_update(state: u32) -> u32 { } pub fn create_flow_array(f0: u32, f1: u32, f2: u32, f3: u32, f4: u32, f5: u32, f6: u32, f7: u32) -> u64 { - return ((((((((() << 56) | (() << 48)) | (() << 40)) | (() << 32)) | (() << 24)) | (() << 16)) | (() << 8)) | ()); + return (((((((((f0 as u64) << 56) | ((f1 as u64) << 48)) | ((f2 as u64) << 40)) | ((f3 as u64) << 32)) | ((f4 as u64) << 24)) | ((f5 as u64) << 16)) | ((f6 as u64) << 8)) | (f7 as u64)); } pub fn get_flow_req(array: u64, index: u32) -> u32 { if (index == 0) { - return (); + return (((array >> 56) & 0xFFFFFFFF) as u32); } if (index == 1) { - return (); + return (((array >> 48) & 0xFFFFFFFF) as u32); } if (index == 2) { - return (); + return (((array >> 40) & 0xFFFFFFFF) as u32); } if (index == 3) { - return (); + return (((array >> 32) & 0xFFFFFFFF) as u32); } if (index == 4) { - return (); + return (((array >> 24) & 0xFFFFFFFF) as u32); } if (index == 5) { - return (); + return (((array >> 16) & 0xFFFFFFFF) as u32); } if (index == 6) { - return (); + return (((array >> 8) & 0xFFFFFFFF) as u32); } - return (); + return ((array & 0xFFFFFFFF) as u32); } pub fn calculate_fair_share(total_bw: u32, flow_count: u32) -> u32 { @@ -86,12 +86,10 @@ pub fn calculate_fair_share(total_bw: u32, flow_count: u32) -> u32 { } pub fn allocate_bandwidth(state: u32, flow_req: u32, available_bw: u32) -> u32 { - let; - let; - let; - allocated = get_allocated_bw(state); - let; - allocation = 0; + let priority = get_flow_priority(flow_req); + let min_bw = get_min_bandwidth(flow_req); + let allocated = get_allocated_bw(state); + let mut allocation = 0; if (priority == 0) { allocation = (min_bw + ((available_bw * 7) / 10)); } else { @@ -110,44 +108,35 @@ pub fn allocate_bandwidth(state: u32, flow_req: u32, available_bw: u32) -> u32 { if (allocation < min_bw) { allocation = min_bw; } - let; - new_allocated = (allocated + allocation); - let; - pending = get_pending_requests(state); - let; - fair_share = get_fair_share(state); - let; - update = get_last_update(state); + let new_allocated = (allocated + allocation); + let pending = get_pending_requests(state); + let fair_share = get_fair_share(state); + let update = get_last_update(state); return create_allocation_state(new_allocated, pending, fair_share, update); } pub fn needs_more_bandwidth(flow_req: u32) -> bool { - let; - current = get_current_bandwidth(flow_req); - let; - min_bw = get_min_bandwidth(flow_req); + let current = get_current_bandwidth(flow_req); + let min_bw = get_min_bandwidth(flow_req); return (current < min_bw); } pub fn update_flow_bandwidth(flow_req: u32, new_bw: u32) -> u32 { - let; - flow_id = get_flow_id(flow_req); - let; - priority = get_flow_priority(flow_req); - let; - min_bw = get_min_bandwidth(flow_req); - if (new_bw < min_bw) { - new_bw = min_bw; + let flow_id = get_flow_id(flow_req); + let priority = get_flow_priority(flow_req); + let min_bw = get_min_bandwidth(flow_req); + let mut clamped_bw = new_bw; + if (clamped_bw < min_bw) { + clamped_bw = min_bw; } - if (new_bw > MAX_BANDWIDTH) { - new_bw = MAX_BANDWIDTH; + if (clamped_bw > MAX_BANDWIDTH) { + clamped_bw = MAX_BANDWIDTH; } - return create_flow_requirement(flow_id, priority, min_bw, new_bw); + return create_flow_requirement(flow_id, priority, min_bw, clamped_bw); } pub fn count_active_flows(flow_array: u64) -> u32 { - let; - count = 0; + let mut count = 0; if (get_current_bandwidth(get_flow_req(flow_array, 0)) > 0) { count = (count + 1); } @@ -176,8 +165,8 @@ pub fn count_active_flows(flow_array: u64) -> u32 { } pub fn find_reclaimable_bandwidth(state: u32, flow_array: u64) -> u32 { - let; - let; + let allocated = get_allocated_bw(state); + let mut total_used = 0; if (get_current_bandwidth(get_flow_req(flow_array, 0)) > 0) { total_used = (total_used + get_current_bandwidth(get_flow_req(flow_array, 0))); } @@ -209,24 +198,15 @@ pub fn find_reclaimable_bandwidth(state: u32, flow_array: u64) -> u32 { } pub fn prioritize_bandwidth(flow_array: u64, available_bw: u32) -> u64 { - let; - remaining_bw = available_bw; - let; - f0 = get_flow_req(flow_array, 0); - let; - f1 = get_flow_req(flow_array, 1); - let; - f2 = get_flow_req(flow_array, 2); - let; - f3 = get_flow_req(flow_array, 3); - let; - f4 = get_flow_req(flow_array, 4); - let; - f5 = get_flow_req(flow_array, 5); - let; - f6 = get_flow_req(flow_array, 6); - let; - f7 = get_flow_req(flow_array, 7); + let remaining_bw = available_bw; + let f0 = get_flow_req(flow_array, 0); + let f1 = get_flow_req(flow_array, 1); + let f2 = get_flow_req(flow_array, 2); + let f3 = get_flow_req(flow_array, 3); + let f4 = get_flow_req(flow_array, 4); + let f5 = get_flow_req(flow_array, 5); + let f6 = get_flow_req(flow_array, 6); + let f7 = get_flow_req(flow_array, 7); return create_flow_array(update_flow_bandwidth(f0, calculate_fair_share(remaining_bw, 8)), update_flow_bandwidth(f1, calculate_fair_share(remaining_bw, 8)), update_flow_bandwidth(f2, calculate_fair_share(remaining_bw, 8)), update_flow_bandwidth(f3, calculate_fair_share(remaining_bw, 8)), update_flow_bandwidth(f4, calculate_fair_share(remaining_bw, 8)), update_flow_bandwidth(f5, calculate_fair_share(remaining_bw, 8)), update_flow_bandwidth(f6, calculate_fair_share(remaining_bw, 8)), update_flow_bandwidth(f7, calculate_fair_share(remaining_bw, 8))); } diff --git a/gen/rust/cache_management.rs b/gen/rust/cache_management.rs index 3387f363..04a9d5af 100644 --- a/gen/rust/cache_management.rs +++ b/gen/rust/cache_management.rs @@ -30,14 +30,10 @@ pub fn get_entry_size(entry: u32) -> u32 { } pub fn update_access_count(entry: u32) -> u32 { - let; - data_id; - let; - access_count; - let; - age; - let; - size; + let data_id: u32 = get_data_id(entry); + let mut access_count: u32 = get_access_count(entry); + let age: u32 = get_age(entry); + let size: u32 = get_entry_size(entry); if (access_count < 255) { access_count = (access_count + 1); } @@ -45,21 +41,16 @@ pub fn update_access_count(entry: u32) -> u32 { } pub fn update_age(entry: u32, new_age: u32) -> u32 { - let; - data_id; - let; - access_count; - let; - size; + let data_id: u32 = get_data_id(entry); + let access_count: u32 = get_access_count(entry); + let size: u32 = get_entry_size(entry); return create_cache_entry(data_id, access_count, new_age, size); } -pub fn find_entry(cache: Vec<>, data_id: u32) -> u32 { - let; - i; +pub fn find_entry(cache: [u32; MAX_ENTRIES as usize], data_id: u32) -> u32 { + let mut i: u32 = 0; while (i < MAX_ENTRIES) { - let; - entry_data_id; + let entry_data_id: u32 = get_data_id(cache[(i) as usize]); if (entry_data_id == data_id) { return i; } @@ -68,9 +59,8 @@ pub fn find_entry(cache: Vec<>, data_id: u32) -> u32 { return MAX_ENTRIES; } -pub fn cache_hit(cache: Vec<>, data_id: u32) -> u32 { - let; - entry_index; +pub fn cache_hit(cache: [u32; MAX_ENTRIES as usize], data_id: u32) -> u32 { + let entry_index: u32 = find_entry(cache, data_id); if (entry_index < MAX_ENTRIES) { return 1; } else { @@ -78,28 +68,24 @@ pub fn cache_hit(cache: Vec<>, data_id: u32) -> u32 { } } -pub fn get_entry(cache: Vec<>, data_id: u32) -> u32 { - let; - entry_index; +pub fn get_entry(cache: [u32; MAX_ENTRIES as usize], data_id: u32) -> u32 { + let entry_index: u32 = find_entry(cache, data_id); if (entry_index < MAX_ENTRIES) { - return cache[entry_index]; + return cache[(entry_index) as usize]; } else { return 0; } } -pub fn add_entry(cache: Vec<>, current_size: u32, data_id: u32, size: u32) -> u32 { - let; - existing_index; +pub fn add_entry(cache: [u32; MAX_ENTRIES as usize], current_size: u32, data_id: u32, size: u32) -> u32 { + let existing_index: u32 = find_entry(cache, data_id); if (existing_index < MAX_ENTRIES) { return current_size; } - let; - empty_index; - let; - i; + let mut empty_index: u32 = MAX_ENTRIES; + let mut i: u32 = 0; while (i < MAX_ENTRIES) { - if (get_data_id(cache[i]) == 0) { + if (get_data_id(cache[(i) as usize]) == 0) { empty_index = i; break; } @@ -110,36 +96,27 @@ pub fn add_entry(cache: Vec<>, current_size: u32, data_id: u32, size: u32) -> u3 if (empty_index == MAX_ENTRIES) { return current_size; } - let; - evicted_size; + let evicted_size: u32 = get_entry_size(cache[(empty_index) as usize]); current_size = (current_size - evicted_size); } if ((current_size + size) > MAX_CACHE_SIZE) { return current_size; } - cache[empty_index] = create_cache_entry(data_id, 1, 0, size); + cache[(empty_index) as usize] = create_cache_entry(data_id, 1, 0, size); return (current_size + size); } -pub fn find_eviction_candidate(cache: Vec<>) -> u32 { - let; - worst_score; - let; - candidate; - let; - i; +pub fn find_eviction_candidate(cache: [u32; MAX_ENTRIES as usize]) -> u32 { + let mut worst_score: u32 = 0xFFFFFFFF; + let mut candidate: u32 = MAX_ENTRIES; + let mut i: u32 = 0; while (i < MAX_ENTRIES) { - let; - entry; - let; - data_id; + let entry: u32 = cache[(i) as usize]; + let data_id: u32 = get_data_id(entry); if (data_id != 0) { - let; - access_count; - let; - age; - let; - score; + let access_count: u32 = get_access_count(entry); + let age: u32 = get_age(entry); + let score: u32 = ((access_count << 8) | age); if (score < worst_score) { worst_score = score; candidate = i; @@ -150,41 +127,35 @@ pub fn find_eviction_candidate(cache: Vec<>) -> u32 { return candidate; } -pub fn remove_entry(cache: Vec<>, current_size: u32, data_id: u32) -> u32 { - let; - entry_index; +pub fn remove_entry(cache: [u32; MAX_ENTRIES as usize], current_size: u32, data_id: u32) -> u32 { + let entry_index: u32 = find_entry(cache, data_id); if (entry_index < MAX_ENTRIES) { - let; - entry_size; - cache[entry_index] = 0; + let entry_size: u32 = get_entry_size(cache[(entry_index) as usize]); + cache[(entry_index) as usize] = 0; return (current_size - entry_size); } else { return current_size; } } -pub fn access_cache(cache: Vec<>, data_id: u32) -> u32 { - let; - entry_index; +pub fn access_cache(cache: [u32; MAX_ENTRIES as usize], data_id: u32) -> u32 { + let entry_index: u32 = find_entry(cache, data_id); if (entry_index < MAX_ENTRIES) { - cache[entry_index] = update_access_count(cache[entry_index]); - cache[entry_index] = update_age(cache[entry_index], 0); + cache[(entry_index) as usize] = update_access_count(cache[(entry_index) as usize]); + cache[(entry_index) as usize] = update_age(cache[(entry_index) as usize], 0); return 1; } else { return 0; } } -pub fn age_cache(cache: Vec<>) -> () { - let; - i; +pub fn age_cache(cache: [u32; MAX_ENTRIES as usize]) -> () { + let mut i: u32 = 0; while (i < MAX_ENTRIES) { - let; - entry; - let; - age; + let entry: u32 = cache[(i) as usize]; + let age: u32 = get_age(entry); if (age < 255) { - cache[i] = update_age(entry, (age + 1)); + cache[(i) as usize] = update_age(entry, (age + 1)); } i = (i + 1); } @@ -202,16 +173,12 @@ pub fn calculate_utilization(current_size: u32) -> u32 { return ((current_size * 100) / MAX_CACHE_SIZE); } -pub fn find_most_popular(cache: Vec<>) -> u32 { - let; - max_access; - let; - popular_index; - let; - i; +pub fn find_most_popular(cache: [u32; MAX_ENTRIES as usize]) -> u32 { + let mut max_access: u32 = 0; + let mut popular_index: u32 = MAX_ENTRIES; + let mut i: u32 = 0; while (i < MAX_ENTRIES) { - let; - access_count; + let access_count: u32 = get_access_count(cache[(i) as usize]); if (access_count > max_access) { max_access = access_count; popular_index = i; @@ -221,20 +188,14 @@ pub fn find_most_popular(cache: Vec<>) -> u32 { return popular_index; } -pub fn find_least_popular(cache: Vec<>) -> u32 { - let; - min_access; - let; - unpopular_index; - let; - i; +pub fn find_least_popular(cache: [u32; MAX_ENTRIES as usize]) -> u32 { + let mut min_access: u32 = 0xFFFFFFFF; + let mut unpopular_index: u32 = MAX_ENTRIES; + let mut i: u32 = 0; while (i < MAX_ENTRIES) { - let; - entry; - let; - data_id; - let; - access_count; + let entry: u32 = cache[(i) as usize]; + let data_id: u32 = get_data_id(entry); + let access_count: u32 = get_access_count(entry); if ((data_id != 0) && (access_count < min_access)) { min_access = access_count; unpopular_index = i; @@ -244,17 +205,13 @@ pub fn find_least_popular(cache: Vec<>) -> u32 { return unpopular_index; } -pub fn should_prefetch(cache: Vec<>, data_id: u32) -> u32 { - let; - popular_index; +pub fn should_prefetch(cache: [u32; MAX_ENTRIES as usize], data_id: u32) -> u32 { + let popular_index: u32 = find_most_popular(cache); if (popular_index < MAX_ENTRIES) { - let; - popular_access; - let; - entry_index; + let popular_access: u32 = get_access_count(cache[(popular_index) as usize]); + let entry_index: u32 = find_entry(cache, data_id); if (entry_index < MAX_ENTRIES) { - let; - access_count; + let access_count: u32 = get_access_count(cache[(entry_index) as usize]); if (access_count >= CACHE_HIT_THRESHOLD) { return 1; } @@ -264,10 +221,8 @@ pub fn should_prefetch(cache: Vec<>, data_id: u32) -> u32 { } pub fn calculate_efficiency(hits: u32, total_accesses: u32, current_size: u32) -> u32 { - let; - hit_rate; - let; - utilization; + let hit_rate: u32 = calculate_hit_rate(hits, total_accesses); + let utilization: u32 = calculate_utilization(current_size); if (utilization > 0) { return ((hit_rate * 100) / utilization); } else { @@ -296,14 +251,10 @@ pub fn get_evictions(stats: u32) -> u32 { } pub fn update_stats(stats: u32, hit: u32, evicted: u32) -> u32 { - let; - hits; - let; - misses; - let; - size; - let; - evictions; + let mut hits: u32 = get_hits(stats); + let mut misses: u32 = get_misses(stats); + let size: u32 = get_cache_size(stats); + let mut evictions: u32 = get_evictions(stats); if (hit == 1) { hits = (hits + 1); } else { diff --git a/gen/rust/compression_engine.rs b/gen/rust/compression_engine.rs index 9555fe2a..7994a804 100644 --- a/gen/rust/compression_engine.rs +++ b/gen/rust/compression_engine.rs @@ -46,17 +46,12 @@ pub fn calculate_compression_ratio(original: u32, compressed: u32) -> u32 { } pub fn compress_rle(data: u32, length: u32) -> u32 { - let; - compressed; - let; - count; - let; - current; - let; - i; + let mut compressed: u32 = 0; + let mut count: u32 = 0; + let mut current: u32 = (data & 0xF); + let mut i: u32 = 0; while ((i < length) && (i < 8)) { - let; - value; + let value: u32 = ((data >> (i * 4)) & 0xF); if (value == current) { count = (count + 1); } else { @@ -73,17 +68,12 @@ pub fn compress_rle(data: u32, length: u32) -> u32 { } pub fn decompress_rle(compressed: u32) -> u32 { - let; - decompressed; - let; - pos; + let mut decompressed: u32 = 0; + let mut pos: u32 = 0; while (pos < 32) { - let; - count; - let; - value; - let; - i; + let count: u32 = ((compressed >> pos) & 0xF); + let value: u32 = ((compressed >> (pos + 4)) & 0xF); + let mut i: u32 = 0; while ((i < count) && (i < 8)) { decompressed = ((decompressed << 4) | value); i = (i + 1); @@ -93,25 +83,17 @@ pub fn decompress_rle(compressed: u32) -> u32 { return decompressed; } -pub fn compress_dictionary(data: u32, dictionary: Vec<>) -> u32 { - let; - best_match; - let; - best_score; - let; - i; +pub fn compress_dictionary(data: u32, dictionary: [u32; DICTIONARY_SIZE as usize]) -> u32 { + let mut best_match: u32 = 0; + let mut best_score: u32 = 0; + let mut i: u32 = 0; while (i < DICTIONARY_SIZE) { - let; - dict_value; - let; - score; - let; - j; + let dict_value: u32 = dictionary[(i) as usize]; + let mut score: u32 = 0; + let mut j: u32 = 0; while (j < 8) { - let; - data_nibble; - let; - dict_nibble; + let data_nibble: u32 = ((data >> (j * 4)) & 0xF); + let dict_nibble: u32 = ((dict_value >> (j * 4)) & 0xF); if (data_nibble == dict_nibble) { score = (score + 1); } @@ -126,17 +108,16 @@ pub fn compress_dictionary(data: u32, dictionary: Vec<>) -> u32 { return best_match; } -pub fn decompress_dictionary(index: u32, dictionary: Vec<>) -> u32 { +pub fn decompress_dictionary(index: u32, dictionary: [u32; DICTIONARY_SIZE as usize]) -> u32 { if (index < DICTIONARY_SIZE) { - return dictionary[index]; + return dictionary[(index) as usize]; } else { return 0; } } pub fn compress_delta(data: u32, previous: u32) -> u32 { - let; - delta; + let mut delta: u32 = 0; if (data > previous) { delta = (data - previous); } else { @@ -154,10 +135,8 @@ pub fn compress_delta(data: u32, previous: u32) -> u32 { } pub fn decompress_delta(encoded: u32, previous: u32) -> u32 { - let; - encoding_type; - let; - value; + let encoding_type: u32 = ((encoded >> 4) & 0x3); + let value: u32 = (encoded & 0xF); if (encoding_type == 0) { if (previous > value) { return (previous - value); @@ -166,16 +145,14 @@ pub fn decompress_delta(encoded: u32, previous: u32) -> u32 { } } else { if (encoding_type == 1) { - let; - delta; + let delta: u32 = (encoded & 0xFF); if (previous > delta) { return (previous - delta); } else { return (previous + delta); } } else { - let; - delta; + let delta: u32 = (encoded & 0xFFF); if (previous > delta) { return (previous - delta); } else { @@ -185,17 +162,12 @@ pub fn decompress_delta(encoded: u32, previous: u32) -> u32 { } } -pub fn choose_compression_method(data: u32, previous: u32, dictionary: Vec<>) -> u32 { - let; - data_nibbles; - let; - rle_compressed; - let; - rle_ratio; - let; - delta_compressed; - let; - delta_size; +pub fn choose_compression_method(data: u32, previous: u32, dictionary: [u32; DICTIONARY_SIZE as usize]) -> u32 { + let data_nibbles: u32 = 8; + let rle_compressed: u32 = compress_rle(data, data_nibbles); + let rle_ratio: u32 = calculate_compression_ratio(data_nibbles, rle_compressed); + let delta_compressed: u32 = compress_delta(data, previous); + let mut delta_size: u32 = 0; if (delta_compressed < 16) { delta_size = 1; } else { @@ -205,8 +177,7 @@ pub fn choose_compression_method(data: u32, previous: u32, dictionary: Vec<>) -> delta_size = 3; } } - let; - delta_ratio; + let delta_ratio: u32 = calculate_compression_ratio(data_nibbles, delta_size); if ((rle_ratio > delta_ratio) && (rle_ratio > 120)) { return METHOD_RLE; } else { @@ -218,13 +189,10 @@ pub fn choose_compression_method(data: u32, previous: u32, dictionary: Vec<>) -> } } -pub fn compress_block(data: u32, previous: u32, dictionary: Vec<>) -> u32 { - let; - method; - let; - compressed; - let; - compressed_size; +pub fn compress_block(data: u32, previous: u32, dictionary: [u32; DICTIONARY_SIZE as usize]) -> u32 { + let method: u32 = choose_compression_method(data, previous, dictionary); + let mut compressed: u32 = 0; + let mut compressed_size: u32 = 8; if (method == METHOD_RLE) { compressed = compress_rle(data, 8); compressed_size = 4; @@ -248,7 +216,7 @@ pub fn compress_block(data: u32, previous: u32, dictionary: Vec<>) -> u32 { return create_block_info(8, compressed_size, method, compressed_size); } -pub fn decompress_block(compressed_data: u32, method: u32, previous: u32, dictionary: Vec<>) -> u32 { +pub fn decompress_block(compressed_data: u32, method: u32, previous: u32, dictionary: [u32; DICTIONARY_SIZE as usize]) -> u32 { if (method == METHOD_RLE) { return decompress_rle(compressed_data); } else { @@ -264,16 +232,13 @@ pub fn decompress_block(compressed_data: u32, method: u32, previous: u32, dictio } } -pub fn calculate_total_savings(blocks: Vec<>, count: u32) -> u32 { - let; - total_original; - let; - total_compressed; - let; - i; +pub fn calculate_total_savings(blocks: [u32; MAX_BLOCKS as usize], count: u32) -> u32 { + let mut total_original: u32 = 0; + let mut total_compressed: u32 = 0; + let mut i: u32 = 0; while (i < count) { - total_original = (total_original + get_original_size(blocks[i])); - total_compressed = (total_compressed + get_compressed_size(blocks[i])); + total_original = (total_original + get_original_size(blocks[(i) as usize])); + total_compressed = (total_compressed + get_compressed_size(blocks[(i) as usize])); i = (i + 1); } if (total_compressed > 0) { @@ -283,9 +248,9 @@ pub fn calculate_total_savings(blocks: Vec<>, count: u32) -> u32 { } } -pub fn update_dictionary(dictionary: Vec<>, new_entry: u32, index: u32) -> u32 { +pub fn update_dictionary(dictionary: [u32; DICTIONARY_SIZE as usize], new_entry: u32, index: u32) -> u32 { if (index < DICTIONARY_SIZE) { - dictionary[index] = new_entry; + dictionary[(index) as usize] = new_entry; return 1; } else { return 0; @@ -293,13 +258,10 @@ pub fn update_dictionary(dictionary: Vec<>, new_entry: u32, index: u32) -> u32 { } pub fn find_pattern(data: u32, pattern: u32) -> u32 { - let; - mask; - let; - i; + let mask: u32 = 0xFFFFFFFF; + let mut i: u32 = 0; while (i < 32) { - let; - shifted; + let shifted: u32 = ((data >> i) & mask); if (shifted == pattern) { return i; } diff --git a/gen/rust/etx.rs b/gen/rust/etx.rs index 81fd58b0..aa46776d 100644 --- a/gen/rust/etx.rs +++ b/gen/rust/etx.rs @@ -29,14 +29,14 @@ pub fn fp_mul(a: u8, b: u8) -> u8 { if ((a == 0) || (b == 0)) { return 0; } - return (); + return ((((a as u16) * (b as u16)) >> 8) as u8); } pub fn ewma_update(est: u8, sample: u8, alpha: u8) -> u8 { if ((est == 255) && (sample == 255)) { return 255; } - return (fp_mul(alpha, sample) + fp_mul((256 - alpha), est)); + return (fp_mul(alpha, sample) + fp_mul(((ONE_FP - (alpha as u16)) as u8), est)); } pub fn is_dead(ratio: u8) -> bool { diff --git a/gen/rust/hardware_validation.rs b/gen/rust/hardware_validation.rs index b9fff529..86ef2bde 100644 --- a/gen/rust/hardware_validation.rs +++ b/gen/rust/hardware_validation.rs @@ -41,6 +41,7 @@ pub fn calculate_pass_rate(passed: u32, total: u32) -> u8 { if (total == 0) { return 0; } + return (((passed * 100) / total) as u8); } pub fn test_passed(result: u32) -> bool { diff --git a/gen/rust/integration_framework.rs b/gen/rust/integration_framework.rs index edde1b60..b80f0c76 100644 --- a/gen/rust/integration_framework.rs +++ b/gen/rust/integration_framework.rs @@ -81,19 +81,14 @@ pub const MSG_ERROR: u32 = 3; pub const MSG_EVENT: u32 = 4; -pub fn send_message(modules: Vec<>, message: u32) -> u32 { - let; - dest; - let; - msg_type; - let; - i; +pub fn send_message(modules: [u32; MAX_MODULES as usize], message: u32) -> u32 { + let dest: u32 = get_integration_message_dest(message); + let msg_type: u32 = get_integration_message_type(message); + let mut i: u32 = 0; while (i < MAX_MODULES) { - let; - module_id; + let module_id: u32 = get_registered_module_id(modules[(i) as usize]); if (module_id == dest) { - let; - status; + let status: u32 = get_registered_module_status(modules[(i) as usize]); if ((status == STATUS_ACTIVE) || (status == STATUS_BUSY)) { return 1; } else { @@ -105,12 +100,10 @@ pub fn send_message(modules: Vec<>, message: u32) -> u32 { return 0; } -pub fn receive_message(messages: Vec<>, message_count: u32, module_id: u32) -> u32 { - let; - i; +pub fn receive_message(messages: [u32; MAX_MESSAGES as usize], message_count: u32, module_id: u32) -> u32 { + let mut i: u32 = 0; while (i < message_count) { - let; - dest; + let dest: u32 = get_integration_message_dest(messages[(i) as usize]); if (dest == module_id) { return i; } @@ -149,14 +142,12 @@ pub const EVENT_SIMULATION_STEP: u32 = 3; pub const EVENT_VISUALIZATION_UPDATE: u32 = 4; -pub fn subscribe_to_event(module_id: u32, event_type: u32, subscriptions: Vec<>) -> u32 { - let; - subscription_id; - let; - i; +pub fn subscribe_to_event(module_id: u32, event_type: u32, subscriptions: [u32; MAX_EVENTS as usize]) -> u32 { + let subscription_id: u32 = ((module_id * 10) + event_type); + let mut i: u32 = 0; while (i < MAX_EVENTS) { - if (subscriptions[i] == 0) { - subscriptions[i] = create_event(subscription_id, event_type, module_id, 0); + if (subscriptions[(i) as usize] == 0) { + subscriptions[(i) as usize] = create_event(subscription_id, event_type, module_id, 0); return 1; } i = (i + 1); @@ -164,23 +155,16 @@ pub fn subscribe_to_event(module_id: u32, event_type: u32, subscriptions: Vec<>) return 0; } -pub fn publish_event(event: u32, subscriptions: Vec<>, modules: Vec<>) -> u32 { - let; - event_type; - let; - notified_count; - let; - i; +pub fn publish_event(event: u32, subscriptions: [u32; MAX_EVENTS as usize], modules: [u32; MAX_MODULES as usize]) -> u32 { + let event_type: u32 = get_event_type(event); + let mut notified_count: u32 = 0; + let mut i: u32 = 0; while (i < MAX_EVENTS) { - let; - sub_event_type; + let sub_event_type: u32 = get_event_type(subscriptions[(i) as usize]); if (sub_event_type == event_type) { - let; - source; - let; - module_id; - let; - msg; + let source: u32 = get_event_source(subscriptions[(i) as usize]); + let module_id: u32 = source; + let msg: u32 = create_integration_message(i, 0, module_id, MSG_EVENT); if (send_message(modules, msg) == 1) { notified_count = (notified_count + 1); } @@ -210,25 +194,17 @@ pub fn get_sync_checksum(sync: u32) -> u32 { return (sync & 0xFF); } -pub fn synchronize_states(modules: Vec<>, module_count: u32, sync_requests: u32) -> u32 { - let; - synced_count; - let; - i; +pub fn synchronize_states(modules: [u32; MAX_MODULES as usize], module_count: u32, sync_requests: u32) -> u32 { + let mut synced_count: u32 = 0; + let mut i: u32 = 0; while (i < module_count) { - let; - module_id; - let; - status; + let module_id: u32 = get_registered_module_id(modules[(i) as usize]); + let status: u32 = get_registered_module_status(modules[(i) as usize]); if ((status == STATUS_ACTIVE) && (sync_requests > 0)) { - let; - state_version; - let; - state_data; - let; - checksum; - let; - sync; + let state_version: u32 = 1; + let state_data: u32 = (i * 10); + let checksum: u32 = ((state_data + state_version) & 0xFF); + let sync: u32 = create_state_sync(module_id, state_version, state_data, checksum); synced_count = (synced_count + 1); } i = (i + 1); @@ -264,27 +240,21 @@ pub const SEVERITY_ERROR: u32 = 2; pub const SEVERITY_CRITICAL: u32 = 3; -pub fn propagate_error(error: u32, modules: Vec<>, module_count: u32) -> u32 { - let; - severity; - let; - notified_count; - let; - i; +pub fn propagate_error(error: u32, modules: [u32; MAX_MODULES as usize], module_count: u32) -> u32 { + let severity: u32 = get_error_severity(error); + let mut notified_count: u32 = 0; + let mut i: u32 = 0; while (i < module_count) { - let; - module_type; + let module_type: u32 = get_registered_module_type(modules[(i) as usize]); if (severity == SEVERITY_CRITICAL) { - let; - msg; + let msg: u32 = create_integration_message(0, 0, i, MSG_ERROR); if (send_message(modules, msg) == 1) { notified_count = (notified_count + 1); } } else { if ((severity == SEVERITY_WARNING) || (severity == SEVERITY_ERROR)) { if ((module_type == TYPE_TESTING) || (module_type == TYPE_SIMULATION)) { - let; - msg; + let msg: u32 = create_integration_message(0, 0, i, MSG_ERROR); if (send_message(modules, msg) == 1) { notified_count = (notified_count + 1); } @@ -296,12 +266,11 @@ pub fn propagate_error(error: u32, modules: Vec<>, module_count: u32) -> u32 { return notified_count; } -pub fn load_module(module_id: u32, module_type: u32, priority: u32, modules: Vec<>) -> u32 { - let; - i; +pub fn load_module(module_id: u32, module_type: u32, priority: u32, modules: [u32; MAX_MODULES as usize]) -> u32 { + let mut i: u32 = 0; while (i < MAX_MODULES) { - if (get_registered_module_id(modules[i]) == 0) { - modules[i] = create_module_registration(module_id, module_type, priority, STATUS_IDLE); + if (get_registered_module_id(modules[(i) as usize]) == 0) { + modules[(i) as usize] = create_module_registration(module_id, module_type, priority, STATUS_IDLE); return 1; } i = (i + 1); @@ -309,14 +278,12 @@ pub fn load_module(module_id: u32, module_type: u32, priority: u32, modules: Vec return 0; } -pub fn unload_module(module_id: u32, modules: Vec<>) -> u32 { - let; - i; +pub fn unload_module(module_id: u32, modules: [u32; MAX_MODULES as usize]) -> u32 { + let mut i: u32 = 0; while (i < MAX_MODULES) { - let; - registered_id; + let registered_id: u32 = get_registered_module_id(modules[(i) as usize]); if (registered_id == module_id) { - modules[i] = 0; + modules[(i) as usize] = 0; return 1; } i = (i + 1); @@ -344,27 +311,19 @@ pub fn get_dependency_required(dep: u32) -> u32 { return (dep & 0xFFF); } -pub fn check_dependencies(module_id: u32, dependencies: Vec<>, loaded_modules: Vec<>, module_count: u32) -> u32 { - let; - satisfied; - let; - i; +pub fn check_dependencies(module_id: u32, dependencies: [u32; MAX_MODULES as usize], loaded_modules: [u32; MAX_MODULES as usize], module_count: u32) -> u32 { + let mut satisfied: u32 = 1; + let mut i: u32 = 0; while (i < MAX_MODULES) { - let; - dep_module_id; + let dep_module_id: u32 = get_dependency_module_id(dependencies[(i) as usize]); if (dep_module_id == module_id) { - let; - depends_on; - let; - required; + let depends_on: u32 = get_dependency_depends_on(dependencies[(i) as usize]); + let required: u32 = get_dependency_required(dependencies[(i) as usize]); if (required == 1) { - let; - j; - let; - found; + let mut j: u32 = 0; + let mut found: u32 = 0; while (j < module_count) { - let; - loaded_id; + let loaded_id: u32 = get_registered_module_id(loaded_modules[(j) as usize]); if (loaded_id == depends_on) { found = 1; break; @@ -409,31 +368,23 @@ pub const RESOURCE_BANDWIDTH: u32 = 2; pub const RESOURCE_STORAGE: u32 = 3; -pub fn allocate_resources(requests: Vec<>, request_count: u32, available_resources: u32) -> u32 { - let; - total_requested; - let; - allocated_count; - let; - i; +pub fn allocate_resources(requests: [u32; MAX_MESSAGES as usize], request_count: u32, available_resources: u32) -> u32 { + let mut total_requested: u32 = 0; + let mut allocated_count: u32 = 0; + let mut i: u32 = 0; while (i < request_count) { - let; - amount; + let amount: u32 = get_resource_request_amount(requests[(i) as usize]); total_requested = (total_requested + amount); i = (i + 1); } if (total_requested <= available_resources) { return total_requested; } else { - let; - allocated; - let; - j; + let mut allocated: u32 = 0; + let mut j: u32 = 0; while ((j < request_count) && (allocated < available_resources)) { - let; - amount; - let; - priority; + let amount: u32 = get_resource_request_amount(requests[(j) as usize]); + let priority: u32 = get_resource_request_priority(requests[(j) as usize]); if ((priority > 7) && ((allocated + amount) <= available_resources)) { allocated = (allocated + amount); allocated_count = (allocated_count + 1); @@ -448,33 +399,25 @@ pub fn create_integration_report(loaded_modules: u32, active_messages: u32, even return (((((loaded_modules & 0xFF) << 24) | ((active_messages & 0xFF) << 16)) | ((events_processed & 0xFF) << 8)) | (errors_handled & 0xFF)); } -pub fn generate_integration_stats(modules: Vec<>, module_count: u32, messages: Vec<>, message_count: u32, events: Vec<>, event_count: u32) -> u32 { - let; - active_modules; - let; - active_messages; - let; - events_processed; - let; - errors_handled; - let; - i; +pub fn generate_integration_stats(modules: [u32; MAX_MODULES as usize], module_count: u32, messages: [u32; MAX_MESSAGES as usize], message_count: u32, events: [u32; MAX_EVENTS as usize], event_count: u32) -> u32 { + let mut active_modules: u32 = 0; + let mut active_messages: u32 = 0; + let mut events_processed: u32 = 0; + let errors_handled: u32 = 0; + let mut i: u32 = 0; while (i < module_count) { - let; - status; + let status: u32 = get_registered_module_status(modules[(i) as usize]); if ((status == STATUS_ACTIVE) || (status == STATUS_BUSY)) { active_modules = (active_modules + 1); } i = (i + 1); } - let; - j; + let mut j: u32 = 0; while (j < message_count) { active_messages = (active_messages + 1); j = (j + 1); } - let; - k; + let mut k: u32 = 0; while (k < event_count) { events_processed = (events_processed + 1); k = (k + 1); @@ -482,16 +425,12 @@ pub fn generate_integration_stats(modules: Vec<>, module_count: u32, messages: V return create_integration_report(active_modules, active_messages, events_processed, errors_handled); } -pub fn validate_integration_health(modules: Vec<>, module_count: u32) -> u32 { - let; - active_count; - let; - error_count; - let; - i; +pub fn validate_integration_health(modules: [u32; MAX_MODULES as usize], module_count: u32) -> u32 { + let mut active_count: u32 = 0; + let mut error_count: u32 = 0; + let mut i: u32 = 0; while (i < module_count) { - let; - status; + let status: u32 = get_registered_module_status(modules[(i) as usize]); if (status == STATUS_ACTIVE) { active_count = (active_count + 1); } else { @@ -501,8 +440,7 @@ pub fn validate_integration_health(modules: Vec<>, module_count: u32) -> u32 { } i = (i + 1); } - let; - active_percentage; + let mut active_percentage: u32 = 0; if (module_count > 0) { active_percentage = ((active_count * 100) / module_count); } diff --git a/gen/rust/link_quality_monitor.rs b/gen/rust/link_quality_monitor.rs index 8cad2c98..307915ed 100644 --- a/gen/rust/link_quality_monitor.rs +++ b/gen/rust/link_quality_monitor.rs @@ -16,38 +16,62 @@ pub const QUALITY_POOR: u8 = 0x60; pub const TREND_THRESHOLD: u8 = 0x05; pub fn update_ewma(current: u8, sample: u8) -> u8 { - let; - term1; - let; - term2; - let; - new_estimate; + let term1: u16 = (((ALPHA_Q8 as u16) * (sample as u16)) >> 8); + let term2: u16 = (((ONE_MINUS_ALPHA_Q8 as u16) * (current as u16)) >> 8); + let new_estimate: u16 = (term1 + term2); + if (new_estimate > 0x280) { + return 0xFF; + } + return (new_estimate as u8); } -pub fn calculate_trend(history: Vec<>) -> i8 { - let; - recent_avg; - let; - older_avg; +pub fn calculate_trend(history: [u8; 8]) -> i8 { + let recent_avg: u8 = ((((((history[7] as u16) + (history[6] as u16)) + (history[5] as u16)) + (history[4] as u16)) >> 2) as u8); + let older_avg: u8 = ((((((history[3] as u16) + (history[2] as u16)) + (history[1] as u16)) + (history[0] as u16)) >> 2) as u8); + if (recent_avg > older_avg) { + return ((recent_avg - older_avg) as i8); + } + return -(((older_avg - recent_avg) as i8)); } pub fn predict_next_etx(current: u8, trend: i8) -> u8 { - let; - prediction; + let prediction: i16 = ((current as i16) + (trend as i16)); + if (prediction < 0x40) { + return 0x40; + } + if (prediction > 0x280) { + return 0xFF; + } + return (prediction as u8); } pub fn is_degrading(current_etx: u8, trend: i8) -> bool { - ((current_etx > QUALITY_POOR) && (trend > TREND_THRESHOLD)); + return ((current_etx > QUALITY_POOR) && (trend > (TREND_THRESHOLD as i8))); } pub fn quality_score(etx: u8, latency_ms: u16) -> u8 { - let; - etx_component; - let; - latency_component; - let; - combined; + let etx_component: u16 = (((etx as u16) * 7) / 10); + let latency_component: u16 = (latency_ms / 100); + let combined: u16 = (etx_component + latency_component); + if (combined > 255) { + return 255; + } + return (combined as u8); } -pub fn classify_quality(score: u8) -> u8 { unimplemented!() } +pub fn classify_quality(score: u8) -> u8 { + if (score <= 50) { + return 0; + } + if (score <= 100) { + return 1; + } + if (score <= 150) { + return 2; + } + if (score <= 200) { + return 3; + } + return 4; +} diff --git a/gen/rust/lite_crypto.rs b/gen/rust/lite_crypto.rs index d9c963fa..c097d7ae 100644 --- a/gen/rust/lite_crypto.rs +++ b/gen/rust/lite_crypto.rs @@ -7,30 +7,29 @@ pub const MD5_BLOCK_SIZE: u32 = 64; pub const CHACHA20_STATE_SIZE: u32 = 16; +pub fn md5_process_block(block: u64, state: u64) -> (u32, u32) { + let compressed = (block ^ state); + return ((((compressed >> 32) & 0xFFFFFFFF) as u32), ((compressed & 0xFFFFFFFF) as u32)); +} + pub fn md5_digest(hash1: u32, hash2: u32) -> u64 { - return ((() << 32) | ()); + return (((hash1 as u64) << 32) | (hash2 as u64)); } -pub fn quarter_round(state: u32, input: u32) -> u32 { - let; - s0 = ((state >> 96) & 0xFFFFFFFF); - let; - s1 = ((state >> 64) & 0xFFFFFFFF); - let; - s2 = ((state >> 32) & 0xFFFFFFFF); - let; - s3 = (state & 0xFFFFFFFF); - let; - c0 = 0x61707865; - let; - c1 = 0x3320646E; - let; - c2 = 0x79622D2E; - let; - let; - let; - let; - let; +pub fn quarter_round(state: u128, input: u128) -> u128 { + let s0 = ((state >> 96) & 0xFFFFFFFF); + let s1 = ((state >> 64) & 0xFFFFFFFF); + let s2 = ((state >> 32) & 0xFFFFFFFF); + let s3 = (state & 0xFFFFFFFF); + let c0 = 0x61707865; + let c1 = 0x3320646E; + let c2 = 0x79622D2E; + let c3 = 0x6B206574; + let new_s0 = ((s0 + input) & 0xFFFFFFFF); + let new_s1 = ((s1 + c0) & 0xFFFFFFFF); + let new_s2 = ((s2 + c1) & 0xFFFFFFFF); + let new_s3 = ((s3 + c2) & 0xFFFFFFFF); + return (((((new_s3 & 0xFFFFFFFF) << 96) | ((new_s2 & 0xFFFFFFFF) << 64)) | ((new_s1 & 0xFFFFFFFF) << 32)) | (new_s0 & 0xFFFFFFFF)); } pub fn generate_psk(seed: u32) -> u32 { diff --git a/gen/rust/m3_multihop.rs b/gen/rust/m3_multihop.rs index b5b75e99..8c533bc9 100644 --- a/gen/rust/m3_multihop.rs +++ b/gen/rust/m3_multihop.rs @@ -20,29 +20,161 @@ pub const ATTEN_MAX: u8 = 30; pub const IPERF3_HDR_LEN: u8 = 8; pub fn iperf3_sequence(packet_byte: u8) -> u32 { - (); + return (packet_byte as u32); } pub fn expected_loss_rate_p10(attenuation_db: u8) -> u8 { - let; - base_loss; - let; - att_factor; - let; - add_loss; - let; - total; + let base_loss: u8 = 0x10; + let att_factor: u8 = ((attenuation_db / 3) as u8); + let add_loss: u8 = (att_factor * 0x10); + let total: u16 = ((base_loss as u16) + (add_loss as u16)); + if (total > 0xC0) { + return 0xC0; + } + return (total as u8); } pub fn throughput_factor_p8(attenuation_db: u8) -> u8 { - let; - loss_p10; - let; - loss_p8; - wrapping_sub(loss_p8); + let loss_p10: u8 = expected_loss_rate_p10(attenuation_db); + let loss_p8: u8 = (((loss_p10 as u16) / 10) as u8); + let inv: u16 = (0x100 - (loss_p8 as u16)); + return (inv as u8); } pub fn signal_quality(attenuation_db: u8) -> u8 { - match; + if (attenuation_db <= 5) { + return 0; + } + if (attenuation_db <= 10) { + return 1; + } + if (attenuation_db <= 15) { + return 2; + } + if (attenuation_db <= 20) { + return 3; + } + if (attenuation_db <= 25) { + return 4; + } + return 5; +} + +pub fn total_attenuation(hop1_db: u8, hop2_db: u8) -> u8 { + let sum: u16 = ((hop1_db as u16) + (hop2_db as u16)); + if (sum > (ATTEN_MAX as u16)) { + return ATTEN_MAX; + } + return (sum as u8); +} + +pub fn delivery_rate_p8(hop1_db: u8, hop2_db: u8) -> u8 { + let factor1: u8 = throughput_factor_p8(hop1_db); + let factor2: u8 = throughput_factor_p8(hop2_db); + let product: u16 = ((factor1 as u16) * (factor2 as u16)); + return ((product >> 8) as u8); +} + +pub fn simulate_hop(attenuation_db: u8, packet_seq: u8) -> bool { + let success_p8: u8 = throughput_factor_p8(attenuation_db); + let random_factor: u8 = (packet_seq % 100); + let random_threshold: u8 = ((((random_factor as u16) * 0x100_) / 100) as u8); + return (random_threshold < success_p8); +} + +pub fn forward_packet(hop1_db: u8, hop2_db: u8, packet_seq: u8) -> bool { + let hop1_ok: bool = simulate_hop(hop1_db, packet_seq); + if hop1_ok { + return simulate_hop(hop2_db, packet_seq); + } + return false; +} + +pub fn tcp_packet_byte(seq: u32, byte_index: u8, data_byte: u8) -> u8 { + if (byte_index == 0) { + return (((seq >> 24) & 0xFF) as u8); + } + if (byte_index == 1) { + return (((seq >> 16) & 0xFF) as u8); + } + if (byte_index == 2) { + return (((seq >> 8) & 0xFF) as u8); + } + if (byte_index == 3) { + return ((seq & 0xFF) as u8); + } + if (byte_index <= 7) { + return 0x00; + } + return 0xAA; +} + +pub fn udp_packet_byte(seq: u16, byte_index: u8, data_byte: u8) -> u8 { + if (byte_index == 0) { + return (((seq >> 8) & 0xFF) as u8); + } + if (byte_index == 1) { + return ((seq & 0xFF) as u8); + } + if (byte_index <= 3) { + return 0x00; + } + return 0xBB; +} + +pub const ST_IDLE: u8 = 0; + +pub const ST_RUNNING: u8 = 1; + +pub const ST_COMPLETE: u8 = 2; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct PerfCounters { + pub packets_sent: u32, + pub packets_delivered: u32, + pub packets_lost: u32, + pub bytes_sent: u32, + pub test_duration_ms: u32, +} + +pub fn calculate_throughput_mbps(counters: PerfCounters) -> u32 { + let bits: u64 = ((counters.bytes_sent as u64) * 8); + let duration_sec: u64 = ((counters.test_duration_ms as u64) / 1000); + if (duration_sec == 0) { + return 0; + } + return (((bits / duration_sec) / 1_000_000) as u32); +} + +pub fn calculate_loss_pct(counters: PerfCounters) -> u8 { + if (counters.packets_sent == 0) { + return 0; + } + let lost: u32 = (counters.packets_sent - counters.packets_delivered); + let loss_p10: u32 = ((lost * 1000) / counters.packets_sent); + return ((loss_p10 / 10) as u8); +} + +pub fn meets_targets(counters: PerfCounters, hop_count: u8) -> bool { + let throughput: u32 = calculate_throughput_mbps(counters.clone()); + let target_throughput: u32 = (TARGET_THROUGHPUT_MBPS * (hop_count as u32)); + let loss_pct: u8 = calculate_loss_pct(counters); + return ((throughput >= target_throughput) && ((loss_pct as u32) < TARGET_PACKET_LOSS_PCT)); +} + +pub fn test_next_state(current_state: u8, test_complete: bool) -> u8 { + if (current_state == ST_IDLE) { + if test_complete { + return ST_COMPLETE; + } + return ST_RUNNING; + } + if (current_state == ST_RUNNING) { + if test_complete { + return ST_COMPLETE; + } + return ST_RUNNING; + } + return ST_IDLE; } diff --git a/gen/rust/mesh_protocol_stack.rs b/gen/rust/mesh_protocol_stack.rs index 3c33ee39..e3eb3ce4 100644 --- a/gen/rust/mesh_protocol_stack.rs +++ b/gen/rust/mesh_protocol_stack.rs @@ -12,7 +12,7 @@ pub const NODE_B: u32 = 2; pub const NODE_C: u32 = 3; pub fn build_packet(src: u32, dst: u32, ttl: u8, payload: u8) -> u32 { - return (((((src & 0xFF) << 24) | ((dst & 0xFF) << 16)) | ((() & 0xF) << 12)) | (() & 0xF)); + return (((((src & 0xFF) << 24) | ((dst & 0xFF) << 16)) | (((ttl as u32) & 0xF) << 12)) | ((payload as u32) & 0xF)); } pub fn extract_src(packet: u32) -> u32 { @@ -24,11 +24,19 @@ pub fn extract_dst(packet: u32) -> u32 { } pub fn extract_ttl(packet: u32) -> u8 { - return (); + return (((packet >> 12) & 0xF) as u8); } pub fn extract_payload(packet: u32) -> u8 { - return (); + return ((packet & 0xF) as u8); +} + +pub fn decrement_ttl(packet: u32) -> (u32, bool) { + if (extract_ttl(packet) > 0) { + return (((packet & 0xFFFF0FFF) | ((((extract_ttl(packet) - 1) as u32) & 0xF) << 12)), false); + } else { + return (packet, true); + } } pub fn route_packet(src: u32, dst: u32, next_hop: u32) -> u32 { @@ -60,10 +68,21 @@ pub fn route_packet(src: u32, dst: u32, next_hop: u32) -> u32 { } pub fn tx_path(src: u32, dst: u32, payload: u8) -> u32 { - return build_packet(src, dst, (), payload); + return build_packet(src, dst, (MAX_HOPS as u8), payload); } pub fn rx_path(packet: u32) -> u8 { return extract_payload(packet); } +pub fn forward_packet(packet: u32, current_node: u32) -> (u32, bool, u32) { + let (forwarded, expired) = decrement_ttl(packet); + if (expired == true) { + return (forwarded, true, 0); + } + if (route_packet(current_node, extract_dst(forwarded), 0) == 0) { + return (forwarded, false, 0); + } + return (forwarded, false, route_packet(current_node, extract_dst(forwarded), 0)); +} + diff --git a/gen/rust/mesh_routing.rs b/gen/rust/mesh_routing.rs index 6b261e23..48b8eb29 100644 --- a/gen/rust/mesh_routing.rs +++ b/gen/rust/mesh_routing.rs @@ -13,6 +13,11 @@ pub const MIN_NODE_ID: u8 = 1; pub const MAX_NODE_ID: u8 = 254; +pub fn mesh_ip(id: u32) -> (u8, u8, u8, u8) { + let node_octet = ((id & 0xFF) as u8); + return (MESH_NET_A, MESH_NET_B, MESH_NET_C, node_octet); +} + pub fn is_mesh_subnet(a: u8, b: u8, c: u8) -> bool { if (a != MESH_NET_A) { return false; @@ -29,7 +34,101 @@ pub fn is_mesh_subnet(a: u8, b: u8, c: u8) -> bool { } } +pub fn node_of_ip(a: u8, b: u8, c: u8, d: u8) -> (u32, bool) { + if !(is_mesh_subnet(a, b, c)) { + return (0, false); + } + if ((d < MIN_NODE_ID) || (d > MAX_NODE_ID)) { + return (0, false); + } + let node_id = (d as u32); + return (node_id, true); +} + +pub fn decrement_ttl(ttl: u8) -> (u8, bool) { + if (ttl == 0) { + return (0, true); + } else { + if (ttl == 1) { + return (0, true); + } else { + return ((ttl - 1), false); + } + } +} + pub fn is_ttl_expired(ttl: u8) -> bool { return (ttl == 0); } +pub fn choose_next_hop(etx_n1: u16, etx_n2: u16, etx_n3: u16, has_n1: bool, has_n2: bool, has_n3: bool) -> (u8, bool) { + let n1_finite = (has_n1 && (etx_n1 != 0xFFFF)); + let n2_finite = (has_n2 && (etx_n2 != 0xFFFF)); + let n3_finite = (has_n3 && (etx_n3 != 0xFFFF)); + if ((n1_finite && n2_finite) && n3_finite) { + if ((etx_n1 <= etx_n2) && (etx_n1 <= etx_n3)) { + return (1, true); + } else { + if ((etx_n2 <= etx_n1) && (etx_n2 <= etx_n3)) { + return (2, true); + } else { + return (3, true); + } + } + } else { + if (n1_finite && n2_finite) { + if (etx_n1 <= etx_n2) { + return (1, true); + } else { + return (2, true); + } + } else { + if (n1_finite && n3_finite) { + if (etx_n1 <= etx_n3) { + return (1, true); + } else { + return (3, true); + } + } else { + if (n2_finite && n3_finite) { + if (etx_n2 <= etx_n3) { + return (2, true); + } else { + return (3, true); + } + } else { + if (n1_finite) != 0 { + return (1, true); + } else { + if (n2_finite) != 0 { + return (2, true); + } else { + if (n3_finite) != 0 { + return (3, true); + } else { + return (0, false); + } + } + } + } + } + } + } +} + +pub fn delivery_decision(is_local: bool, ttl_expired: bool, route_exists: bool, dest_id: u8) -> (u8, u8) { + if is_local { + return (0, 0); + } else { + if ttl_expired { + return (2, 0); + } else { + if !(route_exists) { + return (2, 0); + } else { + return (1, dest_id); + } + } + } +} + diff --git a/gen/rust/multipath_router.rs b/gen/rust/multipath_router.rs index 9125b5e4..f3179358 100644 --- a/gen/rust/multipath_router.rs +++ b/gen/rust/multipath_router.rs @@ -7,43 +7,47 @@ pub const ETX_THRESHOLD_GOOD: u8 = 0x30; pub const ETX_THRESHOLD_POOR: u8 = 0x60; -pub fn select_path_index(etx_values: Vec<>) -> u8 { - let; - min_etx; - let; - mut; - best_idx; +pub fn select_path_index(etx_values: [u8; 3]) -> u8 { + let min_etx: u8 = etx_values[0]; + let mut best_idx: u8 = 0; + if (etx_values[1] < min_etx) { + best_idx = 1; + } + if (etx_values[2] < etx_values[((best_idx as usize)) as usize]) { + best_idx = 2; + } + return best_idx; } pub fn path_quality_score(etx: u8, latency: u16, loss_p8: u8) -> u8 { - let; - etx_component; - let; - latency_component; - let; - loss_component; - let; - total; + let etx_component: u16 = ((etx as u16) * 7); + let latency_component: u16 = ((latency / 10) * 2); + let loss_component: u16 = ((loss_p8 as u16) * 1); + let total: u16 = (((etx_component + latency_component) + loss_component) / 10); + if (total > 255) { + return 255; + } + return (total as u8); } pub fn needs_failover(current_etx: u8, current_idx: u8, max_paths: u8) -> bool { - let; - etx_degraded; - let; - has_backup; - (etx_degraded && has_backup); + let etx_degraded: bool = (current_etx > ETX_THRESHOLD_POOR); + let has_backup: bool = (current_idx < max_paths); + return (etx_degraded && has_backup); } pub fn next_path_index(current_idx: u8, max_paths: u8) -> u8 { - let; - next; + let next: u8 = (current_idx + 1); + if (next >= max_paths) { + return 0; + } + return next; } pub fn path_reliability(etx: u8, loss_rate: u8) -> u8 { - let; - product; - let; - unreliability; - wrapping_sub(unreliability); + let product: u16 = ((etx as u16) * (loss_rate as u16)); + let unreliability: u8 = ((product / 256) as u8); + let full_scale: u8 = 255; + return full_scale.wrapping_sub(unreliability); } diff --git a/gen/rust/multipath_routing.rs b/gen/rust/multipath_routing.rs index 9ac2ae8a..d75f1abf 100644 --- a/gen/rust/multipath_routing.rs +++ b/gen/rust/multipath_routing.rs @@ -52,25 +52,24 @@ pub fn get_multipath_last_update(state: u32) -> u32 { } pub fn create_path_array(p0: u32, p1: u32, p2: u32, p3: u32) -> u64 { - return ((((() << 48) | (() << 32)) | (() << 16)) | ()); + return (((((p0 as u64) << 48) | ((p1 as u64) << 32)) | ((p2 as u64) << 16)) | (p3 as u64)); } pub fn get_multipath(array: u64, index: u32) -> u32 { if (index == 0) { - return (); + return (((array >> 48) & 0xFFFFFFFF) as u32); } if (index == 1) { - return (); + return (((array >> 32) & 0xFFFFFFFF) as u32); } if (index == 2) { - return (); + return (((array >> 16) & 0xFFFFFFFF) as u32); } - return (); + return ((array & 0xFFFFFFFF) as u32); } pub fn count_valid_paths(path_array: u64) -> u32 { - let; - count = 0; + let mut count = 0; if (get_path_valid(get_multipath(path_array, 0)) == PATH_VALID) { count = (count + 1); } @@ -87,11 +86,11 @@ pub fn count_valid_paths(path_array: u64) -> u32 { } pub fn is_multipath_viable(path_array: u64) -> u32 { - return (count_valid_paths(path_array) >= MIN_PATHS); + return ((count_valid_paths(path_array) >= MIN_PATHS)) as u32; } pub fn select_primary_path(path_array: u64, quality_array: u64) -> u32 { - if !(is_multipath_viable(path_array)) { + if ((is_multipath_viable(path_array)) == 0) { return 0xFF; } if (get_path_valid(get_multipath(path_array, 0)) == PATH_VALID) { @@ -110,8 +109,8 @@ pub fn select_primary_path(path_array: u64, quality_array: u64) -> u32 { } pub fn calculate_path_diversity(path_array: u64) -> u32 { - let; - let; + let diversity_score = 0; + let mut hop1_set = 0; if (get_path_valid(get_multipath(path_array, 0)) == PATH_VALID) { hop1_set = (hop1_set | (1 << get_multipath_hop1(get_multipath(path_array, 0)))); } @@ -121,11 +120,10 @@ pub fn calculate_path_diversity(path_array: u64) -> u32 { if (get_path_valid(get_multipath(path_array, 2)) == PATH_VALID) { hop1_set = (hop1_set | (1 << get_multipath_hop1(get_multipath(path_array, 2)))); } - if ((get_path_valid(get_multipath(path_array, 3)) == path_valid) == PATH_VALID) { + if (get_path_valid(get_multipath(path_array, 3)) == PATH_VALID) { hop1_set = (hop1_set | (1 << get_multipath_hop1(get_multipath(path_array, 3)))); } - let; - count = 0; + let mut count = 0; if ((hop1_set & 0x01) == 0x01) { count = (count + 1); } @@ -154,14 +152,13 @@ pub fn calculate_path_diversity(path_array: u64) -> u32 { } pub fn distribute_load(path_array: u64, current_path: u32, load_ratio: u32) -> u32 { - let; - total_paths = count_valid_paths(path_array); + let total_paths = count_valid_paths(path_array); if (total_paths < 2) { return current_path; } - let; - let; - let; + let mut next_path = ((current_path + 1) % total_paths); + let mut found = 0; + let mut attempts = 0; while ((found == 0) && (attempts < 4)) { if (get_path_valid(get_multipath(path_array, next_path)) == PATH_VALID) { found = 1; @@ -184,12 +181,11 @@ pub fn needs_failover(path_array: u64, current_path: u32) -> bool { } pub fn perform_failover(state: u32, path_array: u64, failed_path: u32) -> u32 { - let; - let; - let; + let active = get_active_paths(state); + let current = get_current_path(state); + let flow = get_flow_id(state); if needs_failover(path_array, current) { - let; - backup = distribute_load(path_array, current, 0); + let backup = distribute_load(path_array, current, 0); if ((backup != current) && (backup != 0xFF)) { return create_multipath_state(active, backup, flow, 0); } @@ -198,7 +194,7 @@ pub fn perform_failover(state: u32, path_array: u64, failed_path: u32) -> u32 { } pub fn calculate_multipath_gain(path_array: u64) -> u32 { - let; + let valid_paths = count_valid_paths(path_array); if (valid_paths >= 2) { return (valid_paths * 30); } diff --git a/gen/rust/network_simulator.rs b/gen/rust/network_simulator.rs index f08ab5d7..4524d459 100644 --- a/gen/rust/network_simulator.rs +++ b/gen/rust/network_simulator.rs @@ -70,33 +70,24 @@ pub const NODE_FAILED: u32 = 2; pub const NODE_SLEEPING: u32 = 3; pub fn update_node_status(state: u32, new_status: u32) -> u32 { - let; - node_id; - let; - energy; - let; - position; + let node_id: u32 = get_node_id(state); + let energy: u32 = get_node_energy(state); + let position: u32 = get_node_position(state); return create_node_state(node_id, new_status, energy, position); } pub fn update_node_energy(state: u32, energy_delta: u32) -> u32 { - let; - node_id; - let; - status; - let; - energy; - let; - position; - let; - new_energy; + let node_id: u32 = get_node_id(state); + let status: u32 = get_node_status(state); + let energy: u32 = get_node_energy(state); + let position: u32 = get_node_position(state); + let mut new_energy: u32 = energy; if (energy_delta > energy) { new_energy = 0; } else { new_energy = (energy - energy_delta); } - let; - new_status; + let mut new_status: u32 = status; if ((new_energy == 0) && (status == NODE_ACTIVE)) { new_status = NODE_FAILED; } @@ -124,18 +115,14 @@ pub fn get_link_latency(link: u32) -> u32 { } pub fn update_link_quality(link: u32, new_quality: u32) -> u32 { - let; - source; - let; - dest; - let; - latency; + let source: u32 = get_link_source(link); + let dest: u32 = get_link_dest(link); + let latency: u32 = get_link_latency(link); return create_link_state(source, dest, new_quality, latency); } pub fn is_link_operational(link: u32) -> u32 { - let; - quality; + let quality: u32 = get_link_quality(link); if (quality >= 30) { return 1; } else { @@ -168,16 +155,13 @@ pub fn get_packet_sequence(packet: u32) -> u32 { } pub fn calculate_transmission_time(packet: u32, link: u32) -> u32 { - let; - size; - let; - latency; - let; - transmission_time; + let size: u32 = get_packet_size(packet); + let latency: u32 = get_link_latency(link); + let transmission_time: u32 = (latency + (size / 10)); return transmission_time; } -pub fn create_sim_state(current_time: u32, event_count: u32, node_count: u32, packet_count: u32) -> u32 { +pub fn create_sim_state(current_time: u32, event_count: u32, node_count: u32) -> u32 { return ((((current_time & 0xFFFF) << 16) | ((event_count & 0xFF) << 8)) | (node_count & 0xFF)); } @@ -194,22 +178,16 @@ pub fn get_sim_node_count(state: u32) -> u32 { } pub fn advance_simulation(state: u32, time_delta: u32) -> u32 { - let; - current_time; - let; - event_count; - let; - node_count; - let; - new_time; + let current_time: u32 = get_sim_time(state); + let event_count: u32 = get_sim_event_count(state); + let node_count: u32 = get_sim_node_count(state); + let new_time: u32 = (current_time + time_delta); return create_sim_state(new_time, event_count, node_count); } -pub fn process_event(event: u32, node_states: Vec<>, link_states: Vec<>) -> u32 { - let; - event_type; - let; - node_id; +pub fn process_event(event: u32, node_states: [u32; MAX_NODES as usize], link_states: [u32; MAX_NODES as usize]) -> u32 { + let event_type: u32 = get_event_type(event); + let node_id: u32 = get_event_node_id(event); if (event_type == EVENT_PACKET_SEND) { return 1; } else { @@ -217,9 +195,8 @@ pub fn process_event(event: u32, node_states: Vec<>, link_states: Vec<>) -> u32 return 1; } else { if (event_type == EVENT_NODE_FAILURE) { - let; - current_state; - node_states[node_id] = update_node_status(current_state, NODE_FAILED); + let current_state: u32 = node_states[(node_id) as usize]; + node_states[(node_id) as usize] = update_node_status(current_state, NODE_FAILED); return 1; } else { if (event_type == EVENT_LINK_FAILURE) { @@ -257,10 +234,8 @@ pub fn get_total_latency(stats: u32) -> u32 { } pub fn calculate_delivery_ratio(stats: u32) -> u32 { - let; - sent; - let; - recv; + let sent: u32 = get_packets_sent(stats); + let recv: u32 = get_packets_recv(stats); if (sent > 0) { return ((recv * 100) / sent); } else { @@ -269,10 +244,8 @@ pub fn calculate_delivery_ratio(stats: u32) -> u32 { } pub fn calculate_average_latency(stats: u32) -> u32 { - let; - recv; - let; - total_latency; + let recv: u32 = get_packets_recv(stats); + let total_latency: u32 = get_total_latency(stats); if (recv > 0) { return (total_latency / recv); } else { @@ -281,19 +254,17 @@ pub fn calculate_average_latency(stats: u32) -> u32 { } pub fn create_topology(node_count: u32, density: u32) -> u32 { - let; - link_count; + let mut link_count: u32 = ((node_count * density) / 100); if (link_count > ((node_count * (node_count - 1)) / 2)) { link_count = ((node_count * (node_count - 1)) / 2); } return link_count; } -pub fn inject_fault(fault_type: u32, target_id: u32, node_states: Vec<>) -> u32 { +pub fn inject_fault(fault_type: u32, target_id: u32, node_states: [u32; MAX_NODES as usize]) -> u32 { if (fault_type == EVENT_NODE_FAILURE) { - let; - current_state; - node_states[target_id] = update_node_status(current_state, NODE_FAILED); + let current_state: u32 = node_states[(target_id) as usize]; + node_states[(target_id) as usize] = update_node_status(current_state, NODE_FAILED); return 1; } else { if (fault_type == EVENT_LINK_FAILURE) { @@ -304,32 +275,25 @@ pub fn inject_fault(fault_type: u32, target_id: u32, node_states: Vec<>) -> u32 } } -pub fn run_simulation_step(state: u32, events: Vec<>, event_count: u32, node_states: Vec<>, link_states: Vec<>) -> u32 { - let; - current_time; - let; - processed_count; - let; - i; +pub fn run_simulation_step(state: u32, events: [u32; MAX_EVENTS as usize], event_count: u32, node_states: [u32; MAX_NODES as usize], link_states: [u32; MAX_NODES as usize]) -> u32 { + let current_time: u32 = get_sim_time(state); + let mut processed_count: u32 = 0; + let mut i: u32 = 0; while (i < event_count) { - let; - event_time; + let event_time: u32 = get_event_timestamp(events[(i) as usize]); if (event_time <= current_time) { - process_event(events[i], node_states, link_states); + process_event(events[(i) as usize], node_states, link_states); processed_count = (processed_count + 1); } i = (i + 1); } - let; - new_state; + let new_state: u32 = advance_simulation(state, SIMULATION_TICK_MS); return create_sim_state(get_sim_time(new_state), (get_sim_event_count(new_state) - processed_count), get_sim_node_count(new_state)); } pub fn generate_simulation_report(stats: u32, duration: u32, node_count: u32) -> u32 { - let; - delivery_ratio; - let; - avg_latency; + let delivery_ratio: u32 = calculate_delivery_ratio(stats); + let avg_latency: u32 = calculate_average_latency(stats); return (((((delivery_ratio & 0xFF) << 24) | ((avg_latency & 0xFF) << 16)) | ((duration & 0xFF) << 8)) | (node_count & 0xFF)); } diff --git a/gen/rust/olsr_routing.rs b/gen/rust/olsr_routing.rs index dbcb5b99..41da875d 100644 --- a/gen/rust/olsr_routing.rs +++ b/gen/rust/olsr_routing.rs @@ -64,13 +64,13 @@ pub fn find_index(table: u32, target_id: u32) -> u32 { pub fn set_entry(table: u32, index: u32, new_entry: u32) -> u32 { if (index == 0) { - return ((table & 0xFFFFFFFFFFFFFFFF0000000000000000) | (() << 192)); + return ((((table & 0xFFFFFFFFFFFFFFFF0000000000000000) as u64) | ((new_entry as u64) << 192))) as u32; } else { if (index == 1) { - return ((table & 0xFFFFFFFFFFFFFFFF0000000000000000) | (() << 128)); + return ((((table & 0xFFFFFFFFFFFFFFFF0000000000000000) as u64) | ((new_entry as u64) << 128))) as u32; } else { if (index == 2) { - return ((table & 0xFFFFFFFFFFFFFFFF0000000000000000) | (() << 64)); + return ((((table & 0xFFFFFFFFFFFFFFFF0000000000000000) as u64) | ((new_entry as u64) << 64))) as u32; } else { return (table & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); } @@ -94,24 +94,46 @@ pub fn update_or_add(table: u32, id: u32, quality: u32, time: u32) -> u32 { } } -pub fn get_best_neighbor(table: u32) -> u32 { unimplemented!() } +pub fn get_best_neighbor(table: u32) -> u32 { + if ((get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 1))) && (get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 2)))) { + return get_id(get_entry(table, 0)); + } else { + if ((get_quality(get_entry(table, 1)) >= get_quality(get_entry(table, 0))) && (get_quality(get_entry(table, 1)) >= get_quality(get_entry(table, 2)))) { + return get_id(get_entry(table, 1)); + } else { + return get_id(get_entry(table, 2)); + } + } +} pub fn get_second_best(table: u32, best_id: u32) -> u32 { if (best_id == get_id(get_entry(table, 0))) { + if (get_quality(get_entry(table, 1)) >= get_quality(get_entry(table, 2))) { + return get_id(get_entry(table, 1)); + } + return get_id(get_entry(table, 2)); } else { if (best_id == get_id(get_entry(table, 1))) { + if (get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 2))) { + return get_id(get_entry(table, 0)); + } + return get_id(get_entry(table, 2)); } else { + if (get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 1))) { + return get_id(get_entry(table, 0)); + } + return get_id(get_entry(table, 1)); } } } pub fn select_mprs(table: u32) -> u32 { - let; - best = get_best_neighbor(table); - let; - second = get_second_best(table, best); + let best = get_best_neighbor(table); + let second = get_second_best(table, best); return (((best & 0xFF) << 8) | (second & 0xFF)); } -pub fn count_neighbors(table: u32) -> u32 { unimplemented!() } +pub fn count_neighbors(table: u32) -> u32 { + return (((((get_id_at(table, 0) != 0xFF) as u32) + ((get_id_at(table, 1) != 0xFF) as u32)) + ((get_id_at(table, 2) != 0xFF) as u32)) + ((get_id_at(table, 3) != 0xFF) as u32)); +} diff --git a/gen/rust/packet_queue.rs b/gen/rust/packet_queue.rs index 0ae4f77b..c2dacbd9 100644 --- a/gen/rust/packet_queue.rs +++ b/gen/rust/packet_queue.rs @@ -4,7 +4,7 @@ pub const QUEUE_SIZE: u8 = 8; pub fn get_count(state: u32) -> u8 { - return (); + return (((state >> 6) & 255) as u8); } pub fn is_full(state: u32) -> bool { @@ -27,12 +27,14 @@ pub fn enqueue(state: u32, data: u32) -> u32 { if is_full(state) { return state; } + return (((((state >> 0) & 7) << 0) | ((increment_index((((state >> 3) & 7) as u8)) as u32) << 3)) | (((get_count(state) + 1) as u32) << 6)); } pub fn dequeue(state: u32) -> u32 { if is_empty(state) { return state; } + return ((((increment_index((((state >> 0) & 7) as u8)) as u32) << 0) | (((state >> 3) & 7) << 3)) | (((get_count(state) - 1) as u32) << 6)); } pub fn size(state: u32) -> u8 { diff --git a/gen/rust/pattern_predictor.rs b/gen/rust/pattern_predictor.rs index ccd48925..6d7911d4 100644 --- a/gen/rust/pattern_predictor.rs +++ b/gen/rust/pattern_predictor.rs @@ -44,32 +44,30 @@ pub fn get_trend_direction(storage: u32) -> u32 { } pub fn create_sample_array(s0: u32, s1: u32, s2: u32, s3: u32, s4: u32, s5: u32, s6: u32, s7: u32, s8: u32, s9: u32, s10: u32, s11: u32, s12: u32, s13: u32, s14: u32, s15: u32) -> u64 { - return ((((((((() << 56) | (() << 48)) | (() << 40)) | (() << 32)) | (() << 24)) | (() << 16)) | (() << 8)) | ()); + return (((((((((s0 as u64) << 56) | ((s1 as u64) << 48)) | ((s2 as u64) << 40)) | ((s3 as u64) << 32)) | ((s4 as u64) << 24)) | ((s5 as u64) << 16)) | ((s6 as u64) << 8)) | (s7 as u64)); } -pub fn get_sample_array_upper(array: u64) -> u64 { unimplemented!() } +pub fn get_sample_array_upper(array: u64) -> u64 { + return ((array >> 32) >> 32); +} -pub fn get_sample_array_lower(array: u64) -> u32 { - return (array & 0xFFFFFFFF); +pub fn get_sample_array_lower(array: u64) -> u64 { + return (array & 0xFFFFFFFFFFFFFFFF); } pub fn get_sample_at(array: u64, index: u32) -> u32 { if (index < 8) { - let; - lower = get_sample_array_lower(array); - return (); + let lower = get_sample_array_lower(array); + return (((lower >> ((7 - index) * 8)) & 0xFF) as u32); } else { - let; - upper = get_sample_array_upper(array); - return (); + let upper = get_sample_array_upper(array); + return (((upper >> ((15 - index) * 8)) & 0xFF) as u32); } } pub fn calculate_moving_average(array: u64, window: u32) -> u32 { - let; - sum = 0; - let; - count = window; + let mut sum = 0; + let mut count = window; if (count > 16) { count = 16; } @@ -102,8 +100,8 @@ pub fn detect_trend(array: u64, samples: u32) -> u32 { if (samples < 2) { return 0; } - let; - let; + let first = get_sample_value(get_sample_at(array, 0)); + let last = get_sample_value(get_sample_at(array, (samples - 1))); if (last > (first + 5)) { return 1; } else { @@ -116,14 +114,13 @@ pub fn detect_trend(array: u64, samples: u32) -> u32 { } pub fn predict_next_value(array: u64, samples: u32) -> u32 { - let; - let; + let trend = detect_trend(array, samples); + let current = get_sample_value(get_sample_at(array, (samples - 1))); if (trend == 1) { return (current + 10); } else { if (trend == 2) { - let; - predicted = (current - 10); + let mut predicted = (current - 10); if (predicted < 0) { predicted = 0; } @@ -135,7 +132,7 @@ pub fn predict_next_value(array: u64, samples: u32) -> u32 { } pub fn is_anomalous(array: u64, samples: u32, current_value: u32) -> u32 { - let; + let predicted = predict_next_value(array, samples); if (predicted > current_value) { return (predicted - current_value); } else { @@ -147,18 +144,16 @@ pub fn detect_repeating_pattern(array: u64, samples: u32) -> u32 { if (samples < 4) { return 0; } - let; - let; - let; - let; + let v0 = get_sample_value(get_sample_at(array, 0)); + let v1 = get_sample_value(get_sample_at(array, 1)); + let v2 = get_sample_value(get_sample_at(array, 2)); + let v3 = get_sample_value(get_sample_at(array, 3)); if (((v0 == v2) && (v1 == v3)) && (v0 != v1)) { return 1; } if (samples >= 6) { - let; - v4 = get_sample_value(get_sample_at(array, 4)); - let; - v5 = get_sample_value(get_sample_at(array, 5)); + let v4 = get_sample_value(get_sample_at(array, 4)); + let v5 = get_sample_value(get_sample_at(array, 5)); if (((v0 == v3) && (v1 == v4)) && (v2 == v5)) { return 1; } @@ -170,27 +165,22 @@ pub fn calculate_variance(array: u64, samples: u32) -> u32 { if (samples < 2) { return 0; } - let; - let; - sum_sq_diff = 0; + let avg = calculate_moving_average(array, samples); + let mut sum_sq_diff = 0; if (samples >= 1) { - let; - diff = (get_sample_value(get_sample_at(array, 0)) - avg); + let diff = (get_sample_value(get_sample_at(array, 0)) - avg); sum_sq_diff = (sum_sq_diff + (diff * diff)); } if (samples >= 2) { - let; - diff = (get_sample_value(get_sample_at(array, 1)) - avg); + let diff = (get_sample_value(get_sample_at(array, 1)) - avg); sum_sq_diff = (sum_sq_diff + (diff * diff)); } if (samples >= 3) { - let; - diff = (get_sample_value(get_sample_at(array, 2)) - avg); + let diff = (get_sample_value(get_sample_at(array, 2)) - avg); sum_sq_diff = (sum_sq_diff + (diff * diff)); } if (samples >= 4) { - let; - diff = (get_sample_value(get_sample_at(array, 3)) - avg); + let diff = (get_sample_value(get_sample_at(array, 3)) - avg); sum_sq_diff = (sum_sq_diff + (diff * diff)); } if (samples < 2) { diff --git a/gen/rust/performance_profiler.rs b/gen/rust/performance_profiler.rs index 9807d146..3840f113 100644 --- a/gen/rust/performance_profiler.rs +++ b/gen/rust/performance_profiler.rs @@ -65,16 +65,13 @@ pub fn get_hotspot_impact(hotspot: u32) -> u32 { return (hotspot & 0xFF); } -pub fn calculate_average_cpu(samples: Vec<>, sample_count: u32, func_id: u32) -> u32 { - let; - total_cpu; - let; - matching_samples; - let; - i; +pub fn calculate_average_cpu(samples: [u32; MAX_SAMPLES as usize], sample_count: u32, func_id: u32) -> u32 { + let mut total_cpu: u32 = 0; + let mut matching_samples: u32 = 0; + let mut i: u32 = 0; while (i < sample_count) { - if (get_sample_function_id(samples[i]) == func_id) { - total_cpu = (total_cpu + get_sample_cpu(samples[i])); + if (get_sample_function_id(samples[(i) as usize]) == func_id) { + total_cpu = (total_cpu + get_sample_cpu(samples[(i) as usize])); matching_samples = (matching_samples + 1); } i = (i + 1); @@ -86,16 +83,13 @@ pub fn calculate_average_cpu(samples: Vec<>, sample_count: u32, func_id: u32) -> } } -pub fn calculate_average_memory(samples: Vec<>, sample_count: u32, func_id: u32) -> u32 { - let; - total_memory; - let; - matching_samples; - let; - i; +pub fn calculate_average_memory(samples: [u32; MAX_SAMPLES as usize], sample_count: u32, func_id: u32) -> u32 { + let mut total_memory: u32 = 0; + let mut matching_samples: u32 = 0; + let mut i: u32 = 0; while (i < sample_count) { - if (get_sample_function_id(samples[i]) == func_id) { - total_memory = (total_memory + get_sample_memory(samples[i])); + if (get_sample_function_id(samples[(i) as usize]) == func_id) { + total_memory = (total_memory + get_sample_memory(samples[(i) as usize])); matching_samples = (matching_samples + 1); } i = (i + 1); @@ -107,29 +101,22 @@ pub fn calculate_average_memory(samples: Vec<>, sample_count: u32, func_id: u32) } } -pub fn identify_hotspots(profiles: Vec<>, profile_count: u32) -> u32 { - let; - max_calls; - let; - max_cpu; - let; - hotspot_func; - let; - i; +pub fn identify_hotspots(profiles: [u32; MAX_FUNCTIONS as usize], profile_count: u32) -> u32 { + let mut max_calls: u32 = 0; + let mut max_cpu: u32 = 0; + let mut hotspot_func: u32 = 0; + let mut i: u32 = 0; while (i < profile_count) { - let; - calls; - let; - cpu; + let calls: u32 = get_profile_call_count(profiles[(i) as usize]); + let cpu: u32 = get_profile_total_cpu(profiles[(i) as usize]); if ((calls > max_calls) || ((calls == max_calls) && (cpu > max_cpu))) { max_calls = calls; max_cpu = cpu; - hotspot_func = get_profile_function_id(profiles[i]); + hotspot_func = get_profile_function_id(profiles[(i) as usize]); } i = (i + 1); } - let; - score; + let mut score: u32 = ((max_calls * 10) + max_cpu); if (score > 255) { score = 255; } @@ -140,10 +127,8 @@ pub fn calculate_profiling_overhead(base_runtime: u32, profiled_runtime: u32) -> if (base_runtime == 0) { return 0; } - let; - overhead; - let; - overhead_percentage; + let overhead: u32 = (profiled_runtime - base_runtime); + let overhead_percentage: u32 = ((overhead * 100) / base_runtime); return overhead_percentage; } @@ -175,12 +160,11 @@ pub fn get_allocation_pool(alloc: u32) -> u32 { return (alloc & 0xFF); } -pub fn track_allocation(allocations: Vec<>, alloc_id: u32, size: u32, pool: u32) -> u32 { - let; - i; +pub fn track_allocation(allocations: [u32; MAX_SAMPLES as usize], alloc_id: u32, size: u32, pool: u32) -> u32 { + let mut i: u32 = 0; while (i < MAX_SAMPLES) { - if (get_allocation_id(allocations[i]) == 0) { - allocations[i] = create_allocation(alloc_id, size, 255, pool); + if (get_allocation_id(allocations[(i) as usize]) == 0) { + allocations[(i) as usize] = create_allocation(alloc_id, size, 255, pool); return 1; } i = (i + 1); @@ -188,24 +172,20 @@ pub fn track_allocation(allocations: Vec<>, alloc_id: u32, size: u32, pool: u32) return 0; } -pub fn calculate_total_memory(allocations: Vec<>, sample_count: u32) -> u32 { - let; - total_memory; - let; - i; +pub fn calculate_total_memory(allocations: [u32; MAX_SAMPLES as usize], sample_count: u32) -> u32 { + let mut total_memory: u32 = 0; + let mut i: u32 = 0; while (i < sample_count) { - let; - size; + let size: u32 = get_allocation_size(allocations[(i) as usize]); total_memory = (total_memory + size); i = (i + 1); } return total_memory; } -pub fn detect_memory_leak(allocations: Vec<>, current_count: u32, previous_count: u32) -> u32 { +pub fn detect_memory_leak(allocations: [u32; MAX_SAMPLES as usize], current_count: u32, previous_count: u32) -> u32 { if (current_count > previous_count) { - let; - growth; + let growth: u32 = (current_count - previous_count); if (growth > 5) { return 1; } @@ -233,26 +213,20 @@ pub fn get_stack_cpu_contribution(entry: u32) -> u32 { return (entry & 0xFF); } -pub fn analyze_call_tree(call_stack: Vec<>, stack_size: u32) -> u32 { - let; - max_depth; - let; - total_cpu; - let; - i; +pub fn analyze_call_tree(call_stack: [u32; MAX_SAMPLES as usize], stack_size: u32) -> u32 { + let mut max_depth: u32 = 0; + let mut total_cpu: u32 = 0; + let mut i: u32 = 0; while (i < stack_size) { - let; - depth; - let; - cpu; + let depth: u32 = get_stack_depth(call_stack[(i) as usize]); + let cpu: u32 = get_stack_cpu_contribution(call_stack[(i) as usize]); if (depth > max_depth) { max_depth = depth; } total_cpu = (total_cpu + cpu); i = (i + 1); } - let; - avg_cpu; + let mut avg_cpu: u32 = 0; if (max_depth > 0) { avg_cpu = (total_cpu / max_depth); } @@ -280,20 +254,13 @@ pub fn get_report_overhead(report: u32) -> u32 { } pub fn generate_recommendations(report: u32, hotspot: u32) -> u32 { - let; - total_cpu; - let; - hotspot_score; - let; - overhead; - let; - rec_optimize_cpu; - let; - rec_optimize_memory; - let; - rec_reduce_overhead; - let; - rec_parallelize; + let total_cpu: u32 = get_report_total_cpu(report); + let hotspot_score: u32 = get_hotspot_score(hotspot); + let overhead: u32 = get_report_overhead(report); + let mut rec_optimize_cpu: u32 = 0; + let rec_optimize_memory: u32 = 0; + let mut rec_reduce_overhead: u32 = 0; + let mut rec_parallelize: u32 = 0; if (total_cpu > 80) { rec_optimize_cpu = 1; } @@ -310,10 +277,8 @@ pub fn calculate_improvement_opportunity(current_performance: u32, target_perfor if (current_performance >= target_performance) { return 0; } - let; - gap; - let; - opportunity; + let gap: u32 = (target_performance - current_performance); + let opportunity: u32 = ((gap * 100) / target_performance); return opportunity; } diff --git a/gen/rust/production_deployment.rs b/gen/rust/production_deployment.rs index 935da935..63d4a82f 100644 --- a/gen/rust/production_deployment.rs +++ b/gen/rust/production_deployment.rs @@ -75,7 +75,17 @@ pub fn extract_metrics_enabled(config: u32) -> u32 { return (config & 0xFFFF); } -pub fn create_checklist(power: bool, cooling: bool, network: bool, monitoring: bool) -> u32 { unimplemented!() } +pub fn bool_to_bit(b: bool) -> u32 { + if b { + return 1; + } else { + return 0; + } +} + +pub fn create_checklist(power: bool, cooling: bool, network: bool, monitoring: bool) -> u32 { + return ((((bool_to_bit(power) << 3) | (bool_to_bit(cooling) << 2)) | (bool_to_bit(network) << 1)) | bool_to_bit(monitoring)); +} pub fn checklist_power(checklist: u32) -> bool { return (((checklist >> 3) & 1) == 1); diff --git a/gen/rust/production_scenarios.rs b/gen/rust/production_scenarios.rs index a0509c18..22b1f45c 100644 --- a/gen/rust/production_scenarios.rs +++ b/gen/rust/production_scenarios.rs @@ -12,11 +12,11 @@ pub const STATE_PARTITIONED: u8 = 3; pub const STATE_RECOVERING: u8 = 4; pub fn create_node_state(state: u8, neighbors: u32, uptime: u32) -> u32 { - return ((((() & 0xFF) << 24) | ((neighbors & 0xFF) << 16)) | (uptime & 0xFFFF)); + return (((((state as u32) & 0xFF) << 24) | ((neighbors & 0xFF) << 16)) | (uptime & 0xFFFF)); } pub fn node_state(state: u32) -> u8 { - return (); + return (((state >> 24) & 0xFF) as u8); } pub fn node_neighbors(state: u32) -> u32 { @@ -31,31 +31,31 @@ pub fn cold_start() -> u32 { return create_node_state(STATE_COLD_START, 0, 0); } -pub fn discover_neighbor(node_state: u32) -> u32 { - if (node_state(node_state) == STATE_COLD_START) { - return create_node_state(STATE_DISCOVERING, 0, node_uptime(node_state)); +pub fn discover_neighbor(state: u32) -> u32 { + if (node_state(state) == STATE_COLD_START) { + return create_node_state(STATE_DISCOVERING, 0, node_uptime(state)); } else { - if (node_state(node_state) == STATE_DISCOVERING) { - return create_node_state(STATE_CONNECTED, (node_neighbors(node_state) + 1), node_uptime(node_state)); + if (node_state(state) == STATE_DISCOVERING) { + return create_node_state(STATE_CONNECTED, (node_neighbors(state) + 1), node_uptime(state)); } else { - return node_state; + return state; } } } -pub fn simulate_partition(node_state: u32) -> u32 { - if (node_state(node_state) == STATE_CONNECTED) { - return create_node_state(STATE_PARTITIONED, 0, node_uptime(node_state)); +pub fn simulate_partition(state: u32) -> u32 { + if (node_state(state) == STATE_CONNECTED) { + return create_node_state(STATE_PARTITIONED, 0, node_uptime(state)); } else { - return node_state; + return state; } } -pub fn recover_from_partition(node_state: u32) -> u32 { - if (node_state(node_state) == STATE_PARTITIONED) { - return create_node_state(STATE_RECOVERING, 0, node_uptime(node_state)); +pub fn recover_from_partition(state: u32) -> u32 { + if (node_state(state) == STATE_PARTITIONED) { + return create_node_state(STATE_RECOVERING, 0, node_uptime(state)); } else { - return node_state; + return state; } } @@ -71,11 +71,11 @@ pub fn node_leave(existing_node: u32) -> u32 { } } -pub fn simulate_interference(node_state: u32, interference_level: u8) -> u32 { +pub fn simulate_interference(state: u32, interference_level: u8) -> u32 { if (interference_level > 128) { - return create_node_state(node_state(node_state), 0, node_uptime(node_state)); + return create_node_state(node_state(state), 0, node_uptime(state)); } else { - return node_state; + return state; } } diff --git a/gen/rust/topology_visualizer.rs b/gen/rust/topology_visualizer.rs index 87c32bbd..1eb3ce5e 100644 --- a/gen/rust/topology_visualizer.rs +++ b/gen/rust/topology_visualizer.rs @@ -139,63 +139,45 @@ pub const ALGORITHM_HIERARCHICAL: u32 = 2; pub const ALGORITHM_GRID: u32 = 3; -pub fn calculate_force_layout(nodes: Vec<>, edges: Vec<>, node_count: u32, edge_count: u32, params: u32) -> u32 { - let; - iterations; - let; - temperature; - let; - placed_nodes; - let; - i; +pub fn calculate_force_layout(nodes: [u32; MAX_NODES as usize], edges: [u32; MAX_EDGES as usize], node_count: u32, edge_count: u32, params: u32) -> u32 { + let iterations: u32 = get_layout_iterations(params); + let mut temperature: u32 = get_layout_temperature(params); + let mut placed_nodes: u32 = 0; + let mut i: u32 = 0; while ((i < iterations) && (placed_nodes < node_count)) { - let; - j; + let mut j: u32 = 0; while (j < node_count) { - let; - node_id; - let; - x; - let; - y; - let; - k; + let node_id: u32 = get_viz_node_id(nodes[(j) as usize]); + let x: u32 = get_node_x_position(nodes[(j) as usize]); + let y: u32 = get_node_y_position(nodes[(j) as usize]); + let mut k: u32 = 0; while (k < node_count) { if (k != j) { - let; - other_x; - let; - other_y; - let; - dx; + let other_x: u32 = get_node_x_position(nodes[(k) as usize]); + let other_y: u32 = get_node_y_position(nodes[(k) as usize]); + let mut dx: u32 = 0; if (x > other_x) { dx = (x - other_x); } else { dx = (other_x - x); } - let; - dy; + let mut dy: u32 = 0; if (y > other_y) { dy = (y - other_y); } else { dy = (other_y - y); } - let; - distance; + let distance: u32 = (dx + dy); if (distance < 100) { - let; - force; + let force: u32 = ((100 - distance) / 10); } } k = (k + 1); } - let; - l; + let mut l: u32 = 0; while (l < edge_count) { - let; - source; - let; - dest; + let source: u32 = get_viz_edge_source(edges[(l) as usize]); + let dest: u32 = get_viz_edge_dest(edges[(l) as usize]); if ((source == node_id) || (dest == node_id)) { } l = (l + 1); @@ -211,53 +193,35 @@ pub fn calculate_force_layout(nodes: Vec<>, edges: Vec<>, node_count: u32, edge_ return placed_nodes; } -pub fn calculate_circular_layout(nodes: Vec<>, node_count: u32) -> u32 { - let; - center_x; - let; - center_y; - let; - radius; - let; - i; +pub fn calculate_circular_layout(nodes: [u32; MAX_NODES as usize], node_count: u32) -> u32 { + let center_x: u32 = (CANVAS_SIZE / 2); + let center_y: u32 = (CANVAS_SIZE / 2); + let radius: u32 = (CANVAS_SIZE / 3); + let mut i: u32 = 0; while (i < node_count) { - let; - angle; - let; - x; - let; - y; - let; - node_id; - let; - status; - nodes[i] = create_visual_node(node_id, x, y, status); + let angle: u32 = ((i * 360) / node_count); + let x: u32 = (center_x + ((radius * angle) / 360)); + let y: u32 = (center_y + ((radius * angle) / 360)); + let node_id: u32 = get_viz_node_id(nodes[(i) as usize]); + let status: u32 = get_node_visual_status(nodes[(i) as usize]); + nodes[(i) as usize] = create_visual_node(node_id, x, y, status); i = (i + 1); } return node_count; } -pub fn calculate_hierarchical_layout(nodes: Vec<>, edges: Vec<>, node_count: u32, edge_count: u32) -> u32 { - let; - level_count; - let; - nodes_per_level; - let; - i; - let; - current_level; - let; - nodes_in_level; +pub fn calculate_hierarchical_layout(nodes: [u32; MAX_NODES as usize], edges: [u32; MAX_EDGES as usize], node_count: u32, edge_count: u32) -> u32 { + let level_count: u32 = 4; + let nodes_per_level: u32 = (node_count / level_count); + let mut i: u32 = 0; + let mut current_level: u32 = 0; + let mut nodes_in_level: u32 = 0; while (i < node_count) { - let; - y; - let; - x; - let; - node_id; - let; - status; - nodes[i] = create_visual_node(node_id, x, y, status); + let y: u32 = ((current_level * CANVAS_SIZE) / level_count); + let x: u32 = ((nodes_in_level * CANVAS_SIZE) / nodes_per_level); + let node_id: u32 = get_viz_node_id(nodes[(i) as usize]); + let status: u32 = get_node_visual_status(nodes[(i) as usize]); + nodes[(i) as usize] = create_visual_node(node_id, x, y, status); nodes_in_level = (nodes_in_level + 1); if (nodes_in_level >= nodes_per_level) { nodes_in_level = 0; @@ -268,9 +232,8 @@ pub fn calculate_hierarchical_layout(nodes: Vec<>, edges: Vec<>, node_count: u32 return node_count; } -pub fn apply_layout(nodes: Vec<>, edges: Vec<>, node_count: u32, edge_count: u32, params: u32) -> u32 { - let; - algorithm; +pub fn apply_layout(nodes: [u32; MAX_NODES as usize], edges: [u32; MAX_EDGES as usize], node_count: u32, edge_count: u32, params: u32) -> u32 { + let algorithm: u32 = get_layout_algorithm(params); if (algorithm == ALGORITHM_FORCE_DIRECTED) { return calculate_force_layout(nodes, edges, node_count, edge_count, params); } else { @@ -287,49 +250,35 @@ pub fn apply_layout(nodes: Vec<>, edges: Vec<>, node_count: u32, edge_count: u32 } pub fn render_node(node: u32, size: u32, color: u32) -> u32 { - let; - x; - let; - y; - let; - status; - let; - node_color; + let x: u32 = get_node_x_position(node); + let y: u32 = get_node_y_position(node); + let status: u32 = get_node_visual_status(node); + let node_color: u32 = get_status_color(status); return (((((x & 0xFF) << 24) | ((y & 0xFF) << 16)) | ((size & 0xFF) << 8)) | (node_color & 0xFF)); } -pub fn render_edge(edge: u32, nodes: Vec<>, thickness: u32) -> u32 { - let; - source; - let; - dest; - let; - quality; - let; - source_x; - let; - source_y; - let; - dest_x; - let; - dest_y; - let; - i; +pub fn render_edge(edge: u32, nodes: [u32; MAX_NODES as usize], thickness: u32) -> u32 { + let source: u32 = get_viz_edge_source(edge); + let dest: u32 = get_viz_edge_dest(edge); + let quality: u32 = get_viz_edge_quality(edge); + let mut source_x: u32 = 0; + let mut source_y: u32 = 0; + let mut dest_x: u32 = 0; + let mut dest_y: u32 = 0; + let mut i: u32 = 0; while (i < MAX_NODES) { - let; - node_id; + let node_id: u32 = get_viz_node_id(nodes[(i) as usize]); if (node_id == source) { - source_x = get_node_x_position(nodes[i]); - source_y = get_node_y_position(nodes[i]); + source_x = get_node_x_position(nodes[(i) as usize]); + source_y = get_node_y_position(nodes[(i) as usize]); } if (node_id == dest) { - dest_x = get_node_x_position(nodes[i]); - dest_y = get_node_y_position(nodes[i]); + dest_x = get_node_x_position(nodes[(i) as usize]); + dest_y = get_node_y_position(nodes[(i) as usize]); } i = (i + 1); } - let; - edge_color; + let mut edge_color: u32 = 0; if (quality > 70) { edge_color = COLOR_GREEN; } else { @@ -342,22 +291,17 @@ pub fn render_edge(edge: u32, nodes: Vec<>, thickness: u32) -> u32 { return (((((source_x & 0xFF) << 24) | ((source_y & 0xFF) << 16)) | ((dest_x & 0xFF) << 8)) | (dest_y & 0xFF)); } -pub fn create_visualization_frame(nodes: Vec<>, edges: Vec<>, node_count: u32, edge_count: u32) -> u32 { - let; - frame_size; - let; - i; +pub fn create_visualization_frame(nodes: [u32; MAX_NODES as usize], edges: [u32; MAX_EDGES as usize], node_count: u32, edge_count: u32) -> u32 { + let mut frame_size: u32 = 0; + let mut i: u32 = 0; while (i < node_count) { - let; - rendered; + let rendered: u32 = render_node(nodes[(i) as usize], 20, COLOR_GREEN); frame_size = (frame_size + 1); i = (i + 1); } - let; - j; + let mut j: u32 = 0; while (j < edge_count) { - let; - rendered; + let rendered: u32 = render_edge(edges[(j) as usize], nodes, 2); frame_size = (frame_size + 1); j = (j + 1); } @@ -365,38 +309,28 @@ pub fn create_visualization_frame(nodes: Vec<>, edges: Vec<>, node_count: u32, e } pub fn calculate_viz_complexity(node_count: u32, edge_count: u32) -> u32 { - let; - base_complexity; - let; - rendering_overhead; + let base_complexity: u32 = (node_count + edge_count); + let rendering_overhead: u32 = ((node_count * 10) + (edge_count * 5)); return (base_complexity + rendering_overhead); } pub fn optimize_rendering(node_count: u32, edge_count: u32, target_fps: u32) -> u32 { - let; - complexity; - let; - max_complexity; + let complexity: u32 = calculate_viz_complexity(node_count, edge_count); + let max_complexity: u32 = (1000 / target_fps); if (complexity > max_complexity) { - let; - detail_level; + let detail_level: u32 = ((max_complexity * 100) / complexity); return detail_level; } else { return 100; } } -pub fn generate_topology_visualization(nodes: Vec<>, edges: Vec<>, node_count: u32, edge_count: u32, layout_params: u32) -> u32 { - let; - layout_result; - let; - frame; - let; - fps; - let; - detail_level; - let; - complexity; +pub fn generate_topology_visualization(nodes: [u32; MAX_NODES as usize], edges: [u32; MAX_EDGES as usize], node_count: u32, edge_count: u32, layout_params: u32) -> u32 { + let layout_result: u32 = apply_layout(nodes, edges, node_count, edge_count, layout_params); + let frame: u32 = create_visualization_frame(nodes, edges, node_count, edge_count); + let fps: u32 = 30; + let detail_level: u32 = optimize_rendering(node_count, edge_count, fps); + let complexity: u32 = calculate_viz_complexity(node_count, edge_count); return (((((layout_result & 0xFF) << 24) | ((frame & 0xFF) << 16)) | ((detail_level & 0xFF) << 8)) | (complexity & 0xFF)); } diff --git a/gen/rust/traffic_animator.rs b/gen/rust/traffic_animator.rs index eaf0a1d1..570cacda 100644 --- a/gen/rust/traffic_animator.rs +++ b/gen/rust/traffic_animator.rs @@ -30,16 +30,11 @@ pub fn get_anim_packet_progress(packet: u32) -> u32 { } pub fn update_packet_progress(packet: u32, delta: u32) -> u32 { - let; - packet_id; - let; - source; - let; - dest; - let; - progress; - let; - new_progress; + let packet_id: u32 = get_anim_packet_id(packet); + let source: u32 = get_anim_packet_source(packet); + let dest: u32 = get_anim_packet_dest(packet); + let progress: u32 = get_anim_packet_progress(packet); + let mut new_progress: u32 = (progress + delta); if (new_progress > 100) { new_progress = 100; } @@ -183,18 +178,12 @@ pub fn get_timeline_speed(timeline: u32) -> u32 { } pub fn advance_animation_frame(timeline: u32) -> u32 { - let; - current; - let; - total; - let; - loops; - let; - speed; - let; - new_current; - let; - new_loops; + let current: u32 = get_timeline_current_frame(timeline); + let total: u32 = get_timeline_total_frames(timeline); + let loops: u32 = get_timeline_loop_count(timeline); + let speed: u32 = get_timeline_speed(timeline); + let mut new_current: u32 = (current + speed); + let mut new_loops: u32 = loops; if (new_current >= total) { new_current = 0; new_loops = (loops + 1); @@ -223,15 +212,11 @@ pub fn get_pattern_duration(pattern: u32) -> u32 { } pub fn generate_traffic_burst(pattern: u32, source: u32, dest: u32) -> u32 { - let; - burst_size; - let; - packet_count; - let; - i; + let burst_size: u32 = get_pattern_burst_size(pattern); + let mut packet_count: u32 = 0; + let mut i: u32 = 0; while (i < burst_size) { - let; - packet; + let packet: u32 = create_anim_packet(i, source, dest, 0); packet_count = (packet_count + 1); i = (i + 1); } @@ -239,36 +224,26 @@ pub fn generate_traffic_burst(pattern: u32, source: u32, dest: u32) -> u32 { } pub fn calculate_packet_position(source_x: u32, source_y: u32, dest_x: u32, dest_y: u32, progress: u32) -> u32 { - let; - current_x; - let; - current_y; + let current_x: u32 = (source_x + (((dest_x - source_x) * progress) / 100)); + let current_y: u32 = (source_y + (((dest_y - source_y) * progress) / 100)); return (((current_x & 0xFF) << 24) | ((current_y & 0xFF) << 16)); } -pub fn update_animation_packets(packets: Vec<>, packet_count: u32, speed: u32) -> u32 { - let; - updated_count; - let; - completed_count; - let; - i; +pub fn update_animation_packets(packets: [u32; MAX_PACKETS as usize], packet_count: u32, speed: u32) -> u32 { + let mut updated_count: u32 = 0; + let mut completed_count: u32 = 0; + let mut i: u32 = 0; while (i < packet_count) { - let; - progress; + let progress: u32 = get_anim_packet_progress(packets[(i) as usize]); if (progress < 100) { - let; - new_progress; + let mut new_progress: u32 = (progress + speed); if (new_progress > 100) { new_progress = 100; } - let; - packet_id; - let; - source; - let; - dest; - packets[i] = create_anim_packet(packet_id, source, dest, new_progress); + let packet_id: u32 = get_anim_packet_id(packets[(i) as usize]); + let source: u32 = get_anim_packet_source(packets[(i) as usize]); + let dest: u32 = get_anim_packet_dest(packets[(i) as usize]); + packets[(i) as usize] = create_anim_packet(packet_id, source, dest, new_progress); updated_count = (updated_count + 1); } else { completed_count = (completed_count + 1); @@ -278,100 +253,75 @@ pub fn update_animation_packets(packets: Vec<>, packet_count: u32, speed: u32) - return ((((updated_count & 0xFF) << 24) | ((completed_count & 0xFF) << 16)) | ((packet_count & 0xFF) << 8)); } -pub fn render_animation_frame(packets: Vec<>, packet_count: u32, paths: Vec<>, path_count: u32, frame_id: u32) -> u32 { - let; - timestamp; - let; - duration; +pub fn render_animation_frame(packets: [u32; MAX_PACKETS as usize], packet_count: u32, paths: [u32; MAX_PATHS as usize], path_count: u32, frame_id: u32) -> u32 { + let timestamp: u32 = (frame_id * (1000 / ANIMATION_FPS)); + let duration: u32 = (1000 / ANIMATION_FPS); return create_animation_frame(frame_id, timestamp, packet_count, duration); } pub fn calculate_animation_complexity(packet_count: u32, path_count: u32, node_count: u32) -> u32 { - let; - base_complexity; - let; - rendering_overhead; + let base_complexity: u32 = ((packet_count + path_count) + node_count); + let rendering_overhead: u32 = ((packet_count * 20) + (path_count * 10)); return (base_complexity + rendering_overhead); } pub fn optimize_animation_performance(packet_count: u32, target_fps: u32) -> u32 { - let; - max_packets; + let max_packets: u32 = ((1000 / target_fps) * 2); if (packet_count > max_packets) { - let; - reduction_needed; + let reduction_needed: u32 = (packet_count - max_packets); return reduction_needed; } else { return 0; } } -pub fn generate_traffic_heat_map(packets: Vec<>, packet_count: u32, node_count: u32) -> u32 { - let; - traffic_counts; - 32; - 32; - let; - max_traffic; - let; - i; +pub fn generate_traffic_heat_map(packets: [u32; MAX_PACKETS as usize], packet_count: u32, node_count: u32) -> u32 { + let mut traffic_counts: [u32; 32] = vec![]; + let mut max_traffic: u32 = 0; + let mut i: u32 = 0; while (i < packet_count) { - let; - source; - let; - dest; + let source: u32 = get_anim_packet_source(packets[(i) as usize]); + let dest: u32 = get_anim_packet_dest(packets[(i) as usize]); if (source < 32) { - traffic_counts[source] = (traffic_counts[source] + 1); - if (traffic_counts[source] > max_traffic) { - max_traffic = traffic_counts[source]; + traffic_counts[(source) as usize] = (traffic_counts[(source) as usize] + 1); + if (traffic_counts[(source) as usize] > max_traffic) { + max_traffic = traffic_counts[(source) as usize]; } } if (dest < 32) { - traffic_counts[dest] = (traffic_counts[dest] + 1); - if (traffic_counts[dest] > max_traffic) { - max_traffic = traffic_counts[dest]; + traffic_counts[(dest) as usize] = (traffic_counts[(dest) as usize] + 1); + if (traffic_counts[(dest) as usize] > max_traffic) { + max_traffic = traffic_counts[(dest) as usize]; } } i = (i + 1); } - let; - total_active; - let; - total_traffic; - let; - j; + let mut total_active: u32 = 0; + let mut total_traffic: u32 = 0; + let mut j: u32 = 0; while ((j < node_count) && (j < 32)) { - if (traffic_counts[j] > 0) { + if (traffic_counts[(j) as usize] > 0) { total_active = (total_active + 1); - total_traffic = (total_traffic + traffic_counts[j]); + total_traffic = (total_traffic + traffic_counts[(j) as usize]); } j = (j + 1); } - let; - avg_traffic; + let mut avg_traffic: u32 = 0; if (total_active > 0) { avg_traffic = (total_traffic / total_active); } return ((((max_traffic & 0xFF) << 24) | ((total_active & 0xFF) << 16)) | ((avg_traffic & 0xFF) << 8)); } -pub fn generate_traffic_animation(packets: Vec<>, packet_count: u32, paths: Vec<>, path_count: u32, node_count: u32, duration_frames: u32) -> u32 { - let; - total_frames; - let; - current_frame; - let; - complexity; - let; - optimization; - let; - actual_packet_count; - let; - timeline; - let; - heat_map; - let; - max_traffic; +pub fn generate_traffic_animation(packets: [u32; MAX_PACKETS as usize], packet_count: u32, paths: [u32; MAX_PATHS as usize], path_count: u32, node_count: u32, duration_frames: u32) -> u32 { + let total_frames: u32 = duration_frames; + let current_frame: u32 = 0; + let complexity: u32 = calculate_animation_complexity(packet_count, path_count, node_count); + let optimization: u32 = optimize_animation_performance(packet_count, ANIMATION_FPS); + let actual_packet_count: u32 = (packet_count - optimization); + let timeline: u32 = create_animation_timeline(0, total_frames, 0, 1); + let heat_map: u32 = generate_traffic_heat_map(packets, actual_packet_count, node_count); + let max_traffic: u32 = ((heat_map >> 24) & 0xFF); return (((((total_frames & 0xFF) << 24) | ((complexity & 0xFF) << 16)) | ((actual_packet_count & 0xFF) << 8)) | (max_traffic & 0xFF)); } @@ -380,15 +330,11 @@ pub fn create_animation_controls(play_pause: u32, step_forward: u32, step_backwa } pub fn process_animation_control(control: u32, timeline: u32) -> u32 { - let; - play_pause; - let; - reset; + let play_pause: u32 = ((control >> 3) & 0x1); + let reset: u32 = (control & 0x1); if (reset == 1) { - let; - total_frames; - let; - speed; + let total_frames: u32 = get_timeline_total_frames(timeline); + let speed: u32 = get_timeline_speed(timeline); return create_animation_timeline(0, total_frames, 0, speed); } else { if (play_pause == 1) { @@ -399,18 +345,13 @@ pub fn process_animation_control(control: u32, timeline: u32) -> u32 { } } -pub fn calculate_animation_stats(frames: Vec<>, frame_count: u32) -> u32 { - let; - total_packets; - let; - total_bytes; - let; - avg_latency; - let; - i; +pub fn calculate_animation_stats(frames: [u32; MAX_FRAMES as usize], frame_count: u32) -> u32 { + let mut total_packets: u32 = 0; + let mut total_bytes: u32 = 0; + let mut avg_latency: u32 = 0; + let mut i: u32 = 0; while (i < frame_count) { - let; - packet_count; + let packet_count: u32 = get_anim_frame_packet_count(frames[(i) as usize]); total_packets = (total_packets + packet_count); total_bytes = (total_bytes + (packet_count * 256)); i = (i + 1); diff --git a/gen/rust/trust_manager.rs b/gen/rust/trust_manager.rs index 0386338e..4755b3e8 100644 --- a/gen/rust/trust_manager.rs +++ b/gen/rust/trust_manager.rs @@ -52,29 +52,129 @@ pub fn get_trust_verified(rel: u32) -> u32 { } pub fn create_trust_array(t0: u32, t1: u32, t2: u32, t3: u32, t4: u32, t5: u32, t6: u32, t7: u32) -> u64 { - return ((((((() << 56) | (() << 48)) | (() << 40)) | (() << 32)) | (() << 24)) || (((() << 16) | (() << 8)) | ())); + return ((((((((((t0 as u64) & 0xFF) << 56) | (((t1 as u64) & 0xFF) << 48)) | (((t2 as u64) & 0xFF) << 40)) | (((t3 as u64) & 0xFF) << 32)) | (((t4 as u64) & 0xFF) << 24)) | (((t5 as u64) & 0xFF) << 16)) | (((t6 as u64) & 0xFF) << 8)) | ((t7 as u64) & 0xFF)); } pub fn get_trust_score(array: u64, index: u32) -> u32 { if (index == 0) { - return (); + return (((array >> 56) & 0xFF) as u32); } if (index == 1) { - return (); + return (((array >> 48) & 0xFF) as u32); } if (index == 2) { - return (); + return (((array >> 40) & 0xFF) as u32); } if (index == 3) { - return (); + return (((array >> 32) & 0xFF) as u32); } if (index == 4) { - return (); + return (((array >> 24) & 0xFF) as u32); } if (index == 5) { - return (); + return (((array >> 16) & 0xFF) as u32); } if (index == 6) { + return (((array >> 8) & 0xFF) as u32); } + return ((array & 0xFF) as u32); +} + +pub fn calculate_trust_score(positive: u32, negative: u32) -> u32 { + let total = (positive + negative); + if (total == 0) { + return 50; + } + let mut score = ((positive * 100) / total); + if (score > MAX_TRUST_SCORE) { + score = MAX_TRUST_SCORE; + } + return score; +} + +pub fn update_trust_score(current_score: u32, positive: u32, negative: u32) -> u32 { + let current_positive = get_positive_interactions(current_score); + let current_negative = get_negative_interactions(current_score); + let node_id = get_trust_node_id(current_score); + let new_positive = (current_positive + positive); + let new_negative = (current_negative + negative); + let new_score = calculate_trust_score(new_positive, new_negative); + return create_trust_score(node_id, new_score, new_positive, new_negative); +} + +pub fn is_node_trusted(score: u32) -> bool { + return (get_trust_score_value(score) >= TRUST_THRESHOLD); +} + +pub fn is_node_highly_trusted(score: u32) -> bool { + return (get_trust_score_value(score) >= TRUST_HIGH); +} + +pub fn is_node_low_trusted(score: u32) -> u32 { + return ((get_trust_score_value(score) <= TRUST_LOW)) as u32; +} + +pub fn find_most_trusted(trust_array: u64) -> u32 { + let mut highest_score = 0; + let mut most_trusted = 0xFF; + if (get_trust_score(trust_array, 0) > highest_score) { + highest_score = get_trust_score(trust_array, 0); + most_trusted = 0; + } + if (get_trust_score(trust_array, 1) > highest_score) { + highest_score = get_trust_score(trust_array, 1); + most_trusted = 1; + } + if (get_trust_score(trust_array, 2) > highest_score) { + highest_score = get_trust_score(trust_array, 2); + most_trusted = 2; + } + if (get_trust_score(trust_array, 3) > highest_score) { + highest_score = get_trust_score(trust_array, 3); + most_trusted = 3; + } + if (get_trust_score(trust_array, 4) > highest_score) { + highest_score = get_trust_score(trust_array, 4); + most_trusted = 4; + } + if (get_trust_score(trust_array, 5) > highest_score) { + highest_score = get_trust_score(trust_array, 5); + most_trusted = 5; + } + if (get_trust_score(trust_array, 6) > highest_score) { + highest_score = get_trust_score(trust_array, 6); + most_trusted = 6; + } + if (get_trust_score(trust_array, 7) > highest_score) { + highest_score = get_trust_score(trust_array, 7); + most_trusted = 7; + } + return most_trusted; +} + +pub fn should_route_via_node(trust_array: u64, node_index: u32, min_trust: u32) -> bool { + if (node_index >= MAX_NODES) { + return false; + } + let score = get_trust_score(trust_array, node_index); + return (score >= min_trust); +} + +pub fn penalize_node(current_score: u32, penalty: u32) -> u32 { + let node_id = get_trust_node_id(current_score); + let positive = get_positive_interactions(current_score); + let negative = get_negative_interactions(current_score); + let new_negative = (negative + penalty); + let new_score = calculate_trust_score(positive, new_negative); + return create_trust_score(node_id, new_score, positive, new_negative); +} + +pub fn reward_node(current_score: u32, reward: u32) -> u32 { + let node_id = get_trust_node_id(current_score); + let positive = get_positive_interactions(current_score); + let negative = get_negative_interactions(current_score); + let new_positive = (positive + reward); + let new_score = calculate_trust_score(new_positive, negative); + return create_trust_score(node_id, new_score, new_positive, negative); } diff --git a/specs/adaptive_retry.t27 b/specs/adaptive_retry.t27 index 83f638e1..4d78db74 100644 --- a/specs/adaptive_retry.t27 +++ b/specs/adaptive_retry.t27 @@ -12,67 +12,66 @@ module AdaptiveRetry { const QUALITY_MEDIUM: u8 = 0x80; // 0.5 in Q8 // Calculate exponential backoff delay + // Every condition is parenthesised and every branch returns explicitly: + // that is the subset the t27c parser accepts without dropping statements. fn backoff_delay_ms(attempt: u8) -> u16 { - if attempt == 0 { - BASE_DELAY_MS as u16 - } else if attempt <= 5 { + if (attempt == 0) { + return BASE_DELAY_MS as u16; + } + if (attempt <= 5) { let multiplier: u16 = 1u16 << attempt; let delay: u16 = (BASE_DELAY_MS as u16) * multiplier; - if delay > 5000 { - 5000 - } else { - delay + if (delay > 5000) { + return 5000; } - } else { - 5000 + return delay; } + return 5000; } - + // Determine max retries based on link quality fn max_retries_for_quality(quality_q8: u8) -> u8 { - if quality_q8 >= QUALITY_HIGH { - 5 // High quality: more retries - } else if quality_q8 >= QUALITY_MEDIUM { - 3 // Medium quality: moderate retries - } else { - 1 // Low quality: minimal retries + if (quality_q8 >= QUALITY_HIGH) { + return 5; // High quality: more retries } + if (quality_q8 >= QUALITY_MEDIUM) { + return 3; // Medium quality: moderate retries + } + return 1; // Low quality: minimal retries } - + // Check if retry should be attempted fn should_retry(current_attempt: u8, link_quality_q8: u8) -> bool { let max_retries: u8 = max_retries_for_quality(link_quality_q8); - current_attempt < max_retries + return (current_attempt < max_retries); } - + fn base_probability(quality_q8: u8) -> u8 { - if quality_q8 >= QUALITY_HIGH { - 200 - } else if quality_q8 >= QUALITY_MEDIUM { - 150 - } else { - 100 + if (quality_q8 >= QUALITY_HIGH) { + return 200; + } + if (quality_q8 >= QUALITY_MEDIUM) { + return 150; } + return 100; } fn retry_success_probability(attempt: u8, quality_q8: u8) -> u8 { let base_prob: u8 = base_probability(quality_q8); let decay: u8 = (base_prob / 4) * attempt; - if base_prob > decay { - base_prob - decay - } else { - 10 + if (base_prob > decay) { + return base_prob - decay; } + return 10; } - + // Estimate total retry time for all attempts (recursive, no mutable state) fn total_retry_time(max_retries: u8) -> u16 { - if max_retries == 0 { - 0 - } else { - backoff_delay_ms(max_retries - 1) + total_retry_time(max_retries - 1) + if (max_retries == 0) { + return 0; } + return backoff_delay_ms(max_retries - 1) + total_retry_time(max_retries - 1); } } diff --git a/specs/api_documenter.t27 b/specs/api_documenter.t27 index 9e34ac47..9f20c162 100644 --- a/specs/api_documenter.t27 +++ b/specs/api_documenter.t27 @@ -350,9 +350,15 @@ module api_documenter { } // Generate documentation report + // The report passes its parameter documentation through: the inner call + // used to reuse func_docs (64 slots) where param_docs (16) is expected - + // a size mismatch the generated Rust inherited whole. Threading the two + // parameters keeps every size honest; the function has no callers in + // this spec, so the widened signature breaks nothing. fn generate_documentation_report(func_docs: [u32; MAX_FUNCTIONS], func_count: u32, - xrefs: [u32; MAX_FUNCTIONS], xref_count: u32) -> u32 { - let doc_summary: u32 = generate_api_documentation(func_docs, func_count, func_docs, 0); + xrefs: [u32; MAX_FUNCTIONS], xref_count: u32, + param_docs: [u32; MAX_PARAMETERS], param_count: u32) -> u32 { + let doc_summary: u32 = generate_api_documentation(func_docs, func_count, param_docs, param_count); let documented_funcs: u32 = (doc_summary >> 24) & 0xFF; let coverage: u32 = calculate_documentation_coverage(documented_funcs, func_count); diff --git a/specs/bandwidth_allocator.t27 b/specs/bandwidth_allocator.t27 index d99b35ba..6e222914 100644 --- a/specs/bandwidth_allocator.t27 +++ b/specs/bandwidth_allocator.t27 @@ -133,11 +133,15 @@ module BandwidthAllocator { let flow_id = get_flow_id(flow_req); let priority = get_flow_priority(flow_req); let min_bw = get_min_bandwidth(flow_req); - - if (new_bw < min_bw) { new_bw = min_bw; } - if (new_bw > MAX_BANDWIDTH) { new_bw = MAX_BANDWIDTH; } - - return create_flow_requirement(flow_id, priority, min_bw, new_bw); + + // Parameters are immutable in the generated Rust (the language has + // no mut markers on arguments), so the clamp works on a local copy - + // the same pattern every accumulator in this corpus already uses. + let clamped_bw = new_bw; + if (clamped_bw < min_bw) { clamped_bw = min_bw; } + if (clamped_bw > MAX_BANDWIDTH) { clamped_bw = MAX_BANDWIDTH; } + + return create_flow_requirement(flow_id, priority, min_bw, clamped_bw); } // Count active flows (flows with allocated bandwidth) diff --git a/specs/etx.t27 b/specs/etx.t27 index b43b4f6c..04a78646 100644 --- a/specs/etx.t27 +++ b/specs/etx.t27 @@ -42,7 +42,9 @@ module MeshEtx { if (est == 255 && sample == 255) { return 255; } - return fp_mul(alpha, sample) + fp_mul(256 - alpha, est); + // (1 - alpha) in Q8.8 is 256 - alpha, which needs the u16 width of + // ONE_FP; fp_mul's arguments are u8, so narrow after the subtraction. + return fp_mul(alpha, sample) + fp_mul((ONE_FP - (alpha as u16)) as u8, est); } // Check if delivery ratio is dead diff --git a/specs/hardware_validation.t27 b/specs/hardware_validation.t27 index 3bdc3fa2..927909a5 100644 --- a/specs/hardware_validation.t27 +++ b/specs/hardware_validation.t27 @@ -45,7 +45,7 @@ module HardwareValidation { if (total == 0) { return 0; } - return (((passed * 100) / total) as u8; + return (((passed * 100) / total) as u8); } // Check if test passed (no errors) diff --git a/specs/link_quality_monitor.t27 b/specs/link_quality_monitor.t27 index 830fbefd..5810479e 100644 --- a/specs/link_quality_monitor.t27 +++ b/specs/link_quality_monitor.t27 @@ -16,23 +16,27 @@ module LinkQualityMonitor { const TREND_THRESHOLD: u8 = 0x05; // Small positive trend // Calculate EWMA (Exponentially Weighted Moving Average) - // Formula: est = α·sample + (1-α)·est + // Formula: est = alpha*sample + (1-alpha)*est fn update_ewma(current: u8, sample: u8) -> u8 { // Fixed-point Q8 calculation - // term1 = α * sample + // term1 = alpha * sample let term1: u16 = ((ALPHA_Q8 as u16) * (sample as u16)) >> 8; - - // term2 = (1-α) * current + + // term2 = (1-alpha) * current let term2: u16 = ((ONE_MINUS_ALPHA_Q8 as u16) * (current as u16)) >> 8; - + let new_estimate: u16 = term1 + term2; - - // Cap at reasonable maximum (10.0 in Q8 = 0x280) - if new_estimate > 0x280 { - 0x280 - } else { - new_estimate as u8 + + // Cap at reasonable maximum (10.0 in Q8 = 0x280). + // DEAD BRANCH: term1 <= (0x20 * 0xFF) >> 8 = 31 and + // term2 <= (0xE0 * 0xFF) >> 8 = 223, so new_estimate <= 254 < 0x280. + // Verified by enumerating all 65536 (current, sample) pairs: 0 hits. + // 0x280 is not representable in the u8 return type, so the cap + // saturates to the u8 maximum; unreachable, so no input is affected. + if (new_estimate > 0x280) { + return 0xFF; } + return new_estimate as u8; } // Calculate trend based on historical data @@ -45,11 +49,10 @@ module LinkQualityMonitor { history[1] as u16 + history[0] as u16) >> 2) as u8; // Trend = recent - older (positive = worsening) - if recent_avg > older_avg { - (recent_avg - older_avg) as i8 - } else { - -((older_avg - recent_avg) as i8) + if (recent_avg > older_avg) { + return (recent_avg - older_avg) as i8; } + return -((older_avg - recent_avg) as i8); } // Predict next ETX value based on trend @@ -57,19 +60,23 @@ module LinkQualityMonitor { let prediction: i16 = (current as i16) + (trend as i16); // Ensure reasonable bounds (1.0 to 10.0 in Q8 = 0x40 to 0x280) - if prediction < 0x40 { - 0x40 // Minimum ETX of 1.0 - } else if prediction > 0x280 { - 0x280 // Maximum ETX of 10.0 - } else { - prediction as u8 + if (prediction < 0x40) { + return 0x40; // Minimum ETX of 1.0 } + // DEAD BRANCH: prediction <= 0xFF + 0x7F = 382 < 0x280 (640). + // Verified by enumerating all 65536 (current, trend) pairs: 0 hits. + // 0x280 is not representable in the u8 return type, so the cap + // saturates to the u8 maximum; unreachable, so no input is affected. + if (prediction > 0x280) { + return 0xFF; // Maximum ETX of 10.0 + } + return prediction as u8; } // Determine if link quality is degrading fn is_degrading(current_etx: u8, trend: i8) -> bool { // Degradation criteria: ETX is poor AND trend is positive (worsening) - (current_etx > QUALITY_POOR) && (trend > TREND_THRESHOLD) + return (current_etx > QUALITY_POOR) && (trend > TREND_THRESHOLD); } // Calculate quality score (0-255, lower is better) @@ -80,23 +87,28 @@ module LinkQualityMonitor { let combined: u16 = etx_component + latency_component; - // Cap at 255 - if combined > 255 { 255 } else { combined as u8 } + // Cap at 255 (reachable: combined can reach 833) + if (combined > 255) { + return 255; + } + return combined as u8; } // Convert quality score to classification fn classify_quality(score: u8) -> u8 { - if score <= 50 { - 0 // Excellent - } else if score <= 100 { - 1 // Good - } else if score <= 150 { - 2 // Fair - } else if score <= 200 { - 3 // Poor - } else { - 4 // Very Poor + if (score <= 50) { + return 0; // Excellent + } + if (score <= 100) { + return 1; // Good + } + if (score <= 150) { + return 2; // Fair + } + if (score <= 200) { + return 3; // Poor } + return 4; // Very Poor } } diff --git a/specs/lite_crypto.t27 b/specs/lite_crypto.t27 index 230d4cd8..7b1e3428 100644 --- a/specs/lite_crypto.t27 +++ b/specs/lite_crypto.t27 @@ -13,12 +13,12 @@ module LiteCrypto { // Input: 512-bit block (64 bytes), Output: 128-bit hash // Process single 64-byte block (returns 128-bit hash as tuple) - fn md5_process_block(block: u32, state: u32) -> (u32, u32) { + fn md5_process_block(block: u64, state: u64) -> (u32, u32) { // Simplified: XOR block bytes with state // In real MD5: permutation + rotation + addition // Here: just XOR compression (weak hash, but T27-compliant) let compressed = block ^ state; - return ((compressed >> 32) & 0xFFFFFFFF, compressed & 0xFFFFFFFF); + return (((compressed >> 32) & 0xFFFFFFFF) as u32, (compressed & 0xFFFFFFFF) as u32); } // MD5 final digest (simplified) @@ -29,7 +29,7 @@ module LiteCrypto { // ---- ChaCha20 Quarter-Round (simplified) ---- // State: [s0][s1][s2][s3] (four 32-bit words) - fn quarter_round(state: u32, input: u32) -> u32 { + fn quarter_round(state: u128, input: u128) -> u128 { // ChaCha20 quarter-round from RFC 7539 // Simplified: no add-tweak, no rotate // Just column generation + mixing @@ -54,7 +54,7 @@ module LiteCrypto { return (((new_s3 & 0xFFFFFFFF) << 96) | ((new_s2 & 0xFFFFFFFF) << 64) | ((new_s1 & 0xFFFFFFFF) << 32) | - (new_s0 & 0xFFFFFFFF); + (new_s0 & 0xFFFFFFFF)); } // Generate 128-bit PSK from seed diff --git a/specs/m3_multihop.t27 b/specs/m3_multihop.t27 index de224add..cf0c6c22 100644 --- a/specs/m3_multihop.t27 +++ b/specs/m3_multihop.t27 @@ -1,162 +1,214 @@ // M3 Multi-Hop Mesh Networking - T27 Specification // Implements iperf3-over-2-hops testing with RF attenuation +// +// STRUCTURE NOTE: the test-harness declarations at the bottom of this module +// used to live in a second `module M3TestHarness`. t27c's gen-rust backend +// emits only the FIRST module of a spec, so every declaration in that second +// module (3 constants, the PerfCounters struct and 4 functions) was silently +// dropped from the generated Rust with exit code 0 and empty stderr. They are +// now declared in this single module. Lowered Rust is a flat set of `pub` +// items either way, and the harness functions already referenced M3MultiHop's +// constants unqualified, so the two modules were one scope in practice. No +// computed value changes. +// +// STATEMENT-SHAPE NOTE: t27c's parser discards any statement it cannot parse, +// taking everything up to the next `;` with it. `match`, an `if` with an +// unparenthesised condition, and an implicit tail expression are all lost or +// mislowered. Every conditional below is therefore written as a parenthesised +// condition with an explicit `return` in each branch. The if/else chains are +// ordered so that each arm is only reached when all earlier arms have already +// returned, which reproduces the original `match` arms exactly. module M3MultiHop { // Node IDs for 3-node topology const NODE_A: u32 = 1; // iperf3 server const NODE_B: u32 = 2; // router const NODE_C: u32 = 3; // iperf3 client - + // Performance targets const TARGET_THROUGHPUT_MBPS: u32 = 1; const TARGET_LATENCY_MS: u32 = 10; const TARGET_PACKET_LOSS_PCT: u32 = 5; - + // Attenuation ranges (dB) const ATTEN_MIN: u8 = 0; const ATTEN_MAX: u8 = 30; - + // iperf3 packet header format const IPERF3_HDR_LEN: u8 = 8; - + // Extract iperf3 sequence number from packet fn iperf3_sequence(packet_byte: u8) -> u32 { // First 4 bytes are sequence number (big-endian) // Simplified: just return byte value for demonstration - packet_byte as u32 + return packet_byte as u32; } - + // Calculate expected packet loss rate from attenuation fn expected_loss_rate_p10(attenuation_db: u8) -> u8 { // Fixed-point Q1.7: 1.7 = 1.7% = 0x1D in Q1.7 // Simplified linear model: every 3dB adds ~0.5% loss // Base loss: 0.5% (0x10 in Q1.7) // Additional: (attenuation_db / 3) * 0.5% - + let base_loss: u8 = 0x10; // 0.5% in Q1.7 let att_factor: u8 = (attenuation_db / 3) as u8; let add_loss: u8 = att_factor * 0x10; // 0.5% per 3dB - + // Cap at 15% (0xC0 in Q1.7) let total: u16 = (base_loss as u16) + (add_loss as u16); - if total > 0xC0 { - 0xC0 - } else { - total as u8 + if (total > 0xC0) { + return 0xC0; } + return total as u8; } - + // Calculate throughput factor from attenuation fn throughput_factor_p8(attenuation_db: u8) -> u8 { // Fixed-point Q0.8: 1.0 = 0x100, 0.8 = 0xCC // Factor = 1.0 - (loss_rate / 100.0) - + let loss_p10: u8 = expected_loss_rate_p10(attenuation_db); let loss_p8: u8 = (loss_p10 as u16 / 10) as u8; // Convert Q1.7 to Q0.8 - - // 1.0 - loss_rate in Q0.8 - 0x100_u8.wrapping_sub(loss_p8) + + // 1.0 - loss_rate in Q0.8, truncated back into the u8 Q0.8 register. + // This is the widened form of the original `0x100_u8.wrapping_sub()`: + // 0x100 is 0 in a u8 register, so both spellings agree on all 256 + // possible values of loss_p8 (loss_p8 == 0 gives 0, otherwise + // 256 - loss_p8). The widened form is written out because a method + // call on an out-of-range literal receiver does not survive lowering. + let inv: u16 = 0x100 - (loss_p8 as u16); + return inv as u8; } - + // Get signal quality category fn signal_quality(attenuation_db: u8) -> u8 { // 0 = Excellent, 1 = Good, 2 = Fair, 3 = Poor, 4 = Very Poor, 5 = Extremely Poor - match attenuation_db { - 0..=5 => 0, - 6..=10 => 1, - 11..=15 => 2, - 16..=20 => 3, - 21..=25 => 4, - _ => 5 + // Original match arms: 0..=5, 6..=10, 11..=15, 16..=20, 21..=25, _ + // attenuation_db is unsigned, so an upper-bound test per arm is exact. + if (attenuation_db <= 5) { + return 0; + } + if (attenuation_db <= 10) { + return 1; } + if (attenuation_db <= 15) { + return 2; + } + if (attenuation_db <= 20) { + return 3; + } + if (attenuation_db <= 25) { + return 4; + } + return 5; } - + // Calculate total attenuation for 2-hop path fn total_attenuation(hop1_db: u8, hop2_db: u8) -> u8 { let sum: u16 = (hop1_db as u16) + (hop2_db as u16); - if sum > (ATTEN_MAX as u16) { - ATTEN_MAX - } else { - sum as u8 + if (sum > (ATTEN_MAX as u16)) { + return ATTEN_MAX; } + return sum as u8; } - + // Calculate expected delivery rate for 2-hop path fn delivery_rate_p8(hop1_db: u8, hop2_db: u8) -> u8 { // P_delivered = P_hop1 * P_hop2 // In Q0.8: multiply and shift right by 8 - + let factor1: u8 = throughput_factor_p8(hop1_db); let factor2: u8 = throughput_factor_p8(hop2_db); - + // Multiply Q0.8 values: (a * b) >> 8 let product: u16 = (factor1 as u16) * (factor2 as u16); - (product >> 8) as u8 + return (product >> 8) as u8; } - + // Simulate single hop with attenuation fn simulate_hop(attenuation_db: u8, packet_seq: u8) -> bool { // Calculate success probability let success_p8: u8 = throughput_factor_p8(attenuation_db); - + // Use packet sequence as pseudo-random factor let random_factor: u8 = packet_seq % 100; let random_threshold: u8 = ((random_factor as u16) * 0x100_u16 / 100) as u8; - + // Success if random factor is below success probability - random_threshold < success_p8 + return random_threshold < success_p8; } - + // Simulate 2-hop packet forwarding fn forward_packet(hop1_db: u8, hop2_db: u8, packet_seq: u8) -> bool { - // Try hop 1 - if !simulate_hop(hop1_db, packet_seq) { - false // Lost on first hop - } else { + // Try hop 1. simulate_hop is pure, so binding its result and testing + // the binding is the same computation as testing the call directly, + // and hop 2 is still only attempted when hop 1 succeeded. + let hop1_ok: bool = simulate_hop(hop1_db, packet_seq); + if (hop1_ok) { // Try hop 2 - simulate_hop(hop2_db, packet_seq) + return simulate_hop(hop2_db, packet_seq); } + return false; // Lost on first hop } - + // Generate iperf3 TCP packet byte fn tcp_packet_byte(seq: u32, byte_index: u8, data_byte: u8) -> u8 { // iperf3 TCP format: // [0-3]: sequence number (big-endian) // [4-7]: packet size (big-endian) // [8+]: 0xAA pattern - - match byte_index { - 0 => ((seq >> 24) & 0xFF) as u8, - 1 => ((seq >> 16) & 0xFF) as u8, - 2 => ((seq >> 8) & 0xFF) as u8, - 3 => (seq & 0xFF) as u8, - 4..=7 => 0x00, // Size placeholder - _ => 0xAA // Data pattern + // + // Original match arms: 0, 1, 2, 3, 4..=7, _ + // Indices 0..3 have already returned by the time the <= 7 test runs, + // so that test selects exactly 4..=7. + if (byte_index == 0) { + return ((seq >> 24) & 0xFF) as u8; } + if (byte_index == 1) { + return ((seq >> 16) & 0xFF) as u8; + } + if (byte_index == 2) { + return ((seq >> 8) & 0xFF) as u8; + } + if (byte_index == 3) { + return (seq & 0xFF) as u8; + } + if (byte_index <= 7) { + return 0x00; // Size placeholder + } + return 0xAA; // Data pattern } - + // Generate iperf3 UDP packet byte fn udp_packet_byte(seq: u16, byte_index: u8, data_byte: u8) -> u8 { // iperf3 UDP format: // [0-1]: sequence number (big-endian) // [2-3]: packet size (big-endian) // [4+]: 0xBB pattern - - match byte_index { - 0 => ((seq >> 8) & 0xFF) as u8, - 1 => (seq & 0xFF) as u8, - 2..=3 => 0x00, // Size placeholder - _ => 0xBB // Data pattern + // + // Original match arms: 0, 1, 2..=3, _ + // Indices 0 and 1 have already returned by the time the <= 3 test + // runs, so that test selects exactly 2..=3. + if (byte_index == 0) { + return ((seq >> 8) & 0xFF) as u8; + } + if (byte_index == 1) { + return (seq & 0xFF) as u8; + } + if (byte_index <= 3) { + return 0x00; // Size placeholder } + return 0xBB; // Data pattern } -} -module M3TestHarness { + // ---- Test harness (previously `module M3TestHarness`) ---- + // Test state machine const ST_IDLE: u8 = 0; const ST_RUNNING: u8 = 1; const ST_COMPLETE: u8 = 2; - + // Performance counters struct PerfCounters { packets_sent: u32, @@ -165,58 +217,65 @@ module M3TestHarness { bytes_sent: u32, test_duration_ms: u32, } - + // Calculate throughput in Mbps from counters fn calculate_throughput_mbps(counters: PerfCounters) -> u32 { // Throughput = (bytes_sent * 8) / (duration_sec) // Mbps = ((bytes * 8) / duration) / 1_000_000 - + let bits: u64 = (counters.bytes_sent as u64) * 8; let duration_sec: u64 = (counters.test_duration_ms as u64) / 1000; - - if duration_sec == 0 { - 0 - } else { - // (bits / duration_sec) / 1_000_000 - // Simplified for T27: assume duration is reasonable - ((bits / duration_sec) / 1_000_000) as u32 + + if (duration_sec == 0) { + return 0; } + // (bits / duration_sec) / 1_000_000 + // Simplified for T27: assume duration is reasonable + return ((bits / duration_sec) / 1_000_000) as u32; } - + // Calculate packet loss percentage fn calculate_loss_pct(counters: PerfCounters) -> u8 { - if counters.packets_sent == 0 { - 0 - } else { - let lost: u32 = counters.packets_sent - counters.packets_delivered; - let loss_p10: u32 = (lost * 1000) / counters.packets_sent; - (loss_p10 / 10) as u8 // Convert to percentage + if (counters.packets_sent == 0) { + return 0; } + let lost: u32 = counters.packets_sent - counters.packets_delivered; + let loss_p10: u32 = (lost * 1000) / counters.packets_sent; + return (loss_p10 / 10) as u8; // Convert to percentage } - + // Check if performance meets targets fn meets_targets(counters: PerfCounters, hop_count: u8) -> bool { - let throughput: u32 = calculate_throughput_mbps(counters); + // `counters` is consumed by value twice. PerfCounters lowers to a + // Clone-but-not-Copy Rust struct, so the first use is cloned. A clone + // is field-for-field identical, so both callees see the same values. + let throughput: u32 = calculate_throughput_mbps(counters.clone()); let target_throughput: u32 = TARGET_THROUGHPUT_MBPS * (hop_count as u32); - + let loss_pct: u8 = calculate_loss_pct(counters); - + // Throughput >= target AND loss < target - (throughput >= target_throughput) && (loss_pct < TARGET_PACKET_LOSS_PCT) + return (throughput >= target_throughput) && (loss_pct < TARGET_PACKET_LOSS_PCT); } - + // State transition for test execution fn test_next_state(current_state: u8, test_complete: bool) -> u8 { - match current_state { - ST_IDLE => { - if test_complete { ST_COMPLETE } else { ST_RUNNING } + // Original match arms: ST_IDLE, ST_RUNNING, ST_COMPLETE, _ + // The ST_COMPLETE arm and the wildcard arm both yield ST_IDLE, so one + // trailing `return ST_IDLE;` covers ST_COMPLETE and every other value. + if (current_state == ST_IDLE) { + if (test_complete) { + return ST_COMPLETE; } - ST_RUNNING => { - if test_complete { ST_COMPLETE } else { ST_RUNNING } + return ST_RUNNING; + } + if (current_state == ST_RUNNING) { + if (test_complete) { + return ST_COMPLETE; } - ST_COMPLETE => ST_IDLE, - _ => ST_IDLE + return ST_RUNNING; } + return ST_IDLE; } } @@ -226,15 +285,15 @@ testbench m3_multihop_tb { test expected_loss_rate_calculation { // No attenuation: 0.5% assert M3MultiHop::expected_loss_rate_p10(0) == 0x10; - + // 10dB: ~2.2% assert M3MultiHop::expected_loss_rate_p10(10) > 0x10; assert M3MultiHop::expected_loss_rate_p10(10) < 0x40; - + // 30dB: capped at 15% assert M3MultiHop::expected_loss_rate_p10(30) == 0xC0; } - + // Test signal quality classification test signal_quality_classification { assert M3MultiHop::signal_quality(5) == 0; // Excellent @@ -243,69 +302,69 @@ testbench m3_multihop_tb { assert M3MultiHop::signal_quality(25) == 4; // Very Poor assert M3MultiHop::signal_quality(30) == 5; // Extremely Poor } - + // Test throughput factor calculation test throughput_factor_calculation { // No attenuation: ~100% let factor0: u8 = M3MultiHop::throughput_factor_p8(0); assert factor0 > 0xF0; - + // 10dB: ~98% let factor10: u8 = M3MultiHop::throughput_factor_p8(10); assert factor10 > 0xF0; assert factor10 < 0x100; - + // 30dB: ~85% let factor30: u8 = M3MultiHop::throughput_factor_p8(30); assert factor30 > 0xD0; assert factor30 < 0xF0; } - + // Test total attenuation calculation test total_attenuation_calculation { assert M3MultiHop::total_attenuation(10, 10) == 20; assert M3MultiHop::total_attenuation(15, 15) == 30; assert M3MultiHop::total_attenuation(20, 20) == 30; // Capped } - + // Test delivery rate calculation test delivery_rate_calculation { // No attenuation: ~100% delivery let rate0: u8 = M3MultiHop::delivery_rate_p8(0, 0); assert rate0 > 0xF0; - + // 10dB per hop: ~96% delivery let rate10: u8 = M3MultiHop::delivery_rate_p8(10, 10); assert rate10 > 0xF0; assert rate10 < 0x100; } - + // Test iperf3 TCP packet generation test tcp_packet_generation { let seq: u32 = 0x12345678; - + // Check sequence number bytes assert M3MultiHop::tcp_packet_byte(seq, 0, 0) == 0x12; assert M3MultiHop::tcp_packet_byte(seq, 1, 0) == 0x34; assert M3MultiHop::tcp_packet_byte(seq, 2, 0) == 0x56; assert M3MultiHop::tcp_packet_byte(seq, 3, 0) == 0x78; - + // Check data pattern assert M3MultiHop::tcp_packet_byte(seq, 10, 0) == 0xAA; } - + // Test iperf3 UDP packet generation test udp_packet_generation { let seq: u16 = 0x1234; - + // Check sequence number bytes assert M3MultiHop::udp_packet_byte(seq, 0, 0) == 0x12; assert M3MultiHop::udp_packet_byte(seq, 1, 0) == 0x34; - + // Check data pattern assert M3MultiHop::udp_packet_byte(seq, 10, 0) == 0xBB; } - + // Test hop simulation test hop_simulation { // No attenuation: high success rate @@ -316,7 +375,7 @@ testbench m3_multihop_tb { } } assert success_count > 8; // >80% success - + // High attenuation: lower success rate let mut success_count_high: u8 = 0; for i in 0..10 { @@ -326,7 +385,7 @@ testbench m3_multihop_tb { } assert success_count_high < 8; // More losses } - + // Test 2-hop packet forwarding test two_hop_forwarding { // No attenuation: high success rate @@ -337,7 +396,7 @@ testbench m3_multihop_tb { } } assert success_count > 7; // >70% success - + // High attenuation: lower success rate let mut success_count_high: u8 = 0; for i in 0..10 { diff --git a/specs/mesh_protocol_stack.t27 b/specs/mesh_protocol_stack.t27 index 531e62c1..00d9a4f2 100644 --- a/specs/mesh_protocol_stack.t27 +++ b/specs/mesh_protocol_stack.t27 @@ -77,16 +77,23 @@ module MeshProtocolStack { // Forward packet (decrement TTL, check next hop) // Returns tuple: (new_packet, expired, next_hop) fn forward_packet(packet: u32, current_node: u32) -> (u32, bool, u32) { - if (decrement_ttl(packet).1) { - return (decrement_ttl(packet).0, true, 0); // TTL expired + // decrement_ttl is pure, so binding its result once is identical to + // the four separate calls this used to make. Tuple field access + // (".0" / ".1") is outside the parser's accepted subset and was + // silently discarding this whole body; destructuring carries the + // same two values. + let (forwarded, expired): (u32, bool) = decrement_ttl(packet); + + if (expired == true) { + return (forwarded, true, 0); // TTL expired } - - if (route_packet(current_node, extract_dst(decrement_ttl(packet).0), 0) == 0) { - return (decrement_ttl(packet).0, false, 0); // No route + + if (route_packet(current_node, extract_dst(forwarded), 0) == 0) { + return (forwarded, false, 0); // No route } - - return (decrement_ttl(packet).0, false, - route_packet(current_node, extract_dst(decrement_ttl(packet).0), 0)); // Valid forward + + return (forwarded, false, + route_packet(current_node, extract_dst(forwarded), 0)); // Valid forward } // ---- Tests ---- diff --git a/specs/multipath_router.t27 b/specs/multipath_router.t27 index 616b98aa..a1cbc133 100644 --- a/specs/multipath_router.t27 +++ b/specs/multipath_router.t27 @@ -13,17 +13,17 @@ module MultiPathRouter { fn select_path_index(etx_values: [u8; 3]) -> u8 { // Compare ETX values to find minimum (best quality) let min_etx: u8 = etx_values[0]; - let mut best_idx: u8 = 0; - - if etx_values[1] < min_etx { + var best_idx: u8 = 0; + + if (etx_values[1] < min_etx) { best_idx = 1; } - - if etx_values[2] < etx_values[best_idx as usize] { + + if (etx_values[2] < etx_values[best_idx as usize]) { best_idx = 2; } - - best_idx + + return best_idx; } // Calculate path quality score (lower is better) @@ -38,7 +38,10 @@ module MultiPathRouter { let total: u16 = (etx_component + latency_component + loss_component) / 10; // Cap at reasonable maximum - if total > 255 { 255 } else { total as u8 } + if (total > 255) { + return 255; + } + return (total as u8); } // Decide if failover is needed @@ -46,18 +49,17 @@ module MultiPathRouter { // Need failover if: ETX degraded OR not on primary path let etx_degraded: bool = current_etx > ETX_THRESHOLD_POOR; let has_backup: bool = current_idx < max_paths; - - etx_degraded && has_backup + + return (etx_degraded && has_backup); } // Calculate next path index with wrap-around fn next_path_index(current_idx: u8, max_paths: u8) -> u8 { let next: u8 = current_idx + 1; - if next >= max_paths { - 0 // Wrap back to primary - } else { - next + if (next >= max_paths) { + return 0; // Wrap back to primary } + return next; } // Estimate path reliability based on metrics @@ -69,7 +71,8 @@ module MultiPathRouter { let unreliability: u8 = (product / 256) as u8; // Invert (255 - x) for reliability - 255_u8.wrapping_sub(unreliability) + let full_scale: u8 = 255; + return full_scale.wrapping_sub(unreliability); } } diff --git a/specs/multipath_routing.t27 b/specs/multipath_routing.t27 index b9843fa6..a20862dc 100644 --- a/specs/multipath_routing.t27 +++ b/specs/multipath_routing.t27 @@ -125,7 +125,7 @@ module multipath_routing { hop1_set = hop1_set | (1 << get_multipath_hop1(get_multipath(path_array, 2))); } - if (get_path_valid(get_multipath(path_array, 3)) == path_valid == PATH_VALID) { + if (get_path_valid(get_multipath(path_array, 3)) == PATH_VALID) { hop1_set = hop1_set | (1 << get_multipath_hop1(get_multipath(path_array, 3))); } diff --git a/specs/network_simulator.t27 b/specs/network_simulator.t27 index a73e98c5..234cb250 100644 --- a/specs/network_simulator.t27 +++ b/specs/network_simulator.t27 @@ -187,8 +187,10 @@ module network_simulator { return transmission_time; } - // Simulation state [current_time][event_count][node_count][packet_count] - fn create_sim_state(current_time: u32, event_count: u32, node_count: u32, packet_count: u32) -> u32 { + // Simulation state [current_time:16][event_count:8][node_count:8] + // The 32-bit word has no room for a packet count and no accessor reads one, + // so the state constructor takes exactly the three fields it packs. + fn create_sim_state(current_time: u32, event_count: u32, node_count: u32) -> u32 { return (((current_time & 0xFFFF) << 16) | ((event_count & 0xFF) << 8) | (node_count & 0xFF)); diff --git a/specs/olsr_routing.t27 b/specs/olsr_routing.t27 index 12595aac..e9065058 100644 --- a/specs/olsr_routing.t27 +++ b/specs/olsr_routing.t27 @@ -93,10 +93,10 @@ module OlsrRouting { // Get best neighbor fn get_best_neighbor(table: u32) -> u32 { if ((get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 1))) && - (get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 2))) { + (get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 2)))) { return get_id(get_entry(table, 0)); } else if ((get_quality(get_entry(table, 1)) >= get_quality(get_entry(table, 0))) && - (get_quality(get_entry(table, 1)) >= get_quality(get_entry(table, 2))) { + (get_quality(get_entry(table, 1)) >= get_quality(get_entry(table, 2)))) { return get_id(get_entry(table, 1)); } else { return get_id(get_entry(table, 2)); @@ -107,19 +107,19 @@ module OlsrRouting { fn get_second_best(table: u32, best_id: u32) -> u32 { if (best_id == get_id(get_entry(table, 0))) { // Exclude n0 - if ((get_quality(get_entry(table, 1)) >= get_quality(get_entry(table, 2))) { + if ((get_quality(get_entry(table, 1)) >= get_quality(get_entry(table, 2)))) { return get_id(get_entry(table, 1)); } return get_id(get_entry(table, 2)); } else if (best_id == get_id(get_entry(table, 1))) { // Exclude n1 - if ((get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 2))) { + if ((get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 2)))) { return get_id(get_entry(table, 0)); } return get_id(get_entry(table, 2)); } else { // Exclude n2 or n3 - if ((get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 1))) { + if ((get_quality(get_entry(table, 0)) >= get_quality(get_entry(table, 1)))) { return get_id(get_entry(table, 0)); } return get_id(get_entry(table, 1)); @@ -135,10 +135,13 @@ module OlsrRouting { // Count neighbors fn count_neighbors(table: u32) -> u32 { - return (if get_id_at(table, 0) != 0xFF { 1 } else { 0 }) + - (if get_id_at(table, 1) != 0xFF { 1 } else { 0 }) + - (if get_id_at(table, 2) != 0xFF { 1 } else { 0 }) + - (if get_id_at(table, 3) != 0xFF { 1 } else { 0 }); + // Same value as the four-way sum of "1 if occupied else 0": a bool + // cast to u32 is 1 when true and 0 when false, so each term is + // identical to the if-expression it replaces. + return (((get_id_at(table, 0) != 0xFF) as u32) + + ((get_id_at(table, 1) != 0xFF) as u32) + + ((get_id_at(table, 2) != 0xFF) as u32) + + ((get_id_at(table, 3) != 0xFF) as u32)); } // ---- Tests ---- diff --git a/specs/packet_queue.t27 b/specs/packet_queue.t27 index 8890e88c..0a979a38 100644 --- a/specs/packet_queue.t27 +++ b/specs/packet_queue.t27 @@ -30,7 +30,7 @@ module PacketQueue { return state; } - return ((((state >> 0) & 7) << 0) | (((increment_index(((state >> 3) & 7) as u8) as u32) << 3) | ((((get_count(state) + 1) as u32) << 6)); + return ((((state >> 0) & 7) << 0) | ((increment_index(((state >> 3) & 7) as u8) as u32) << 3) | (((get_count(state) + 1) as u32) << 6)); } fn dequeue(state: u32) -> u32 { @@ -38,7 +38,7 @@ module PacketQueue { return state; } - return ((((increment_index(((state >> 0) & 7) as u8) as u32) << 0) | (((state >> 3) & 7) << 3) | ((((get_count(state) - 1) as u32) << 6)); + return (((increment_index(((state >> 0) & 7) as u8) as u32) << 0) | (((state >> 3) & 7) << 3) | (((get_count(state) - 1) as u32) << 6)); } fn size(state: u32) -> u8 { diff --git a/specs/pattern_predictor.t27 b/specs/pattern_predictor.t27 index 9491adcb..9109481e 100644 --- a/specs/pattern_predictor.t27 +++ b/specs/pattern_predictor.t27 @@ -65,12 +65,25 @@ module pattern_predictor { (s7 as u64)); } + // Slots 8..15 of the conceptual 16-sample array. create_sample_array + // packs s0..s7 into bits 63..0 of the u64 and discards s8..s15, so the + // half that would hold slots 8..15 does not exist in a u64 and is + // always empty. This is the original expression (array >> 64) with the + // shift re-associated so that no single shift reaches the width of the + // type: (x >> 32) >> 32 equals x >> 64 for every u64 x, and equals 0. + // The 64-bit mask was a no-op on a u64 and is dropped. fn get_sample_array_upper(array: u64) -> u64 { - return ((array >> 64) & 0xFFFFFFFFFFFFFFFF; + return ((array >> 32) >> 32); } - fn get_sample_array_lower(array: u64) -> u32 { - return (array & 0xFFFFFFFF); + // Slots 0..7. get_sample_at reads this half with shifts (7 - index) * 8, + // i.e. 56, 48, 40, 32, 24, 16, 8, 0, which is the exact set of bit + // positions create_sample_array writes s0..s7 to. That requires all 64 + // bits, so the half is a u64 masked with 64 bits, not a u32 masked with + // 32. The previous u32 form kept only s4..s7 and made the shifts for + // index 0..3 (56/48/40/32) exceed the width of a u32. + fn get_sample_array_lower(array: u64) -> u64 { + return (array & 0xFFFFFFFFFFFFFFFF); } fn get_sample_at(array: u64, index: u32) -> u32 { diff --git a/specs/production_deployment.t27 b/specs/production_deployment.t27 index e1aca23d..7e2708b9 100644 --- a/specs/production_deployment.t27 +++ b/specs/production_deployment.t27 @@ -85,12 +85,21 @@ module ProductionDeployment { return (config & 0xFFFF); } + // Convert a bool to its 0/1 bit value + fn bool_to_bit(b: bool) -> u32 { + if (b) { + return 1; + } else { + return 0; + } + } + // Field deployment checklist fn create_checklist(power: bool, cooling: bool, network: bool, monitoring: bool) -> u32 { - return ((if power { 1u32 } else { 0u32 }) << 3) | - ((if cooling { 1u32 } else { 0u32 }) << 2) | - ((if network { 1u32 } else { 0u32 }) << 1) | - (if monitoring { 1u32 } else { 0u32 }); + return ((bool_to_bit(power) << 3) | + (bool_to_bit(cooling) << 2) | + (bool_to_bit(network) << 1) | + bool_to_bit(monitoring)); } fn checklist_power(checklist: u32) -> bool { @@ -164,9 +173,31 @@ module ProductionDeployment { assert(extract_metrics_enabled(config) == 0xFF, "metrics"); } + test bool_to_bit_values { + assert(bool_to_bit(true) == 1, "true is 1"); + assert(bool_to_bit(false) == 0, "false is 0"); + } + test create_checklist_all_true { checklist = create_checklist(true, true, true, true); assert(checklist_complete(checklist) == true, "all items"); + assert(checklist == 0xF, "packed value"); + } + + test create_checklist_none { + checklist = create_checklist(false, false, false, false); + assert(checklist == 0, "empty checklist"); + assert(checklist_power(checklist) == false, "no power"); + assert(checklist_cooling(checklist) == false, "no cooling"); + assert(checklist_network(checklist) == false, "no network"); + assert(checklist_monitoring(checklist) == false, "no monitoring"); + } + + test create_checklist_bit_weights { + assert(create_checklist(true, false, false, false) == 8, "power weight"); + assert(create_checklist(false, true, false, false) == 4, "cooling weight"); + assert(create_checklist(false, false, true, false) == 2, "network weight"); + assert(create_checklist(false, false, false, true) == 1, "monitoring weight"); } test checklist_power_true { diff --git a/specs/production_scenarios.t27 b/specs/production_scenarios.t27 index cc816cfa..78b7d1a2 100644 --- a/specs/production_scenarios.t27 +++ b/specs/production_scenarios.t27 @@ -33,32 +33,32 @@ module ProductionScenarios { return create_node_state(STATE_COLD_START, 0, 0); } - fn discover_neighbor(node_state: u32) -> u32 { - if (node_state(node_state) == STATE_COLD_START) { - return create_node_state(STATE_DISCOVERING, 0, node_uptime(node_state)); - } else if (node_state(node_state) == STATE_DISCOVERING) { + fn discover_neighbor(state: u32) -> u32 { + if (node_state(state) == STATE_COLD_START) { + return create_node_state(STATE_DISCOVERING, 0, node_uptime(state)); + } else if (node_state(state) == STATE_DISCOVERING) { return create_node_state(STATE_CONNECTED, - (node_neighbors(node_state) + 1), - node_uptime(node_state)); + (node_neighbors(state) + 1), + node_uptime(state)); } else { - return node_state; + return state; } } // Network partition simulation - fn simulate_partition(node_state: u32) -> u32 { - if (node_state(node_state) == STATE_CONNECTED) { - return create_node_state(STATE_PARTITIONED, 0, node_uptime(node_state)); + fn simulate_partition(state: u32) -> u32 { + if (node_state(state) == STATE_CONNECTED) { + return create_node_state(STATE_PARTITIONED, 0, node_uptime(state)); } else { - return node_state; + return state; } } - fn recover_from_partition(node_state: u32) -> u32 { - if (node_state(node_state) == STATE_PARTITIONED) { - return create_node_state(STATE_RECOVERING, 0, node_uptime(node_state)); + fn recover_from_partition(state: u32) -> u32 { + if (node_state(state) == STATE_PARTITIONED) { + return create_node_state(STATE_RECOVERING, 0, node_uptime(state)); } else { - return node_state; + return state; } } @@ -80,12 +80,12 @@ module ProductionScenarios { } // Radio interference simulation - fn simulate_interference(node_state: u32, interference_level: u8) -> u32 { + fn simulate_interference(state: u32, interference_level: u8) -> u32 { if (interference_level > 128) { // High interference - lose neighbors - return create_node_state(node_state(node_state), 0, node_uptime(node_state)); + return create_node_state(node_state(state), 0, node_uptime(state)); } else { - return node_state; + return state; } } diff --git a/specs/trust_manager.t27 b/specs/trust_manager.t27 index 3099b454..81b1cea5 100644 --- a/specs/trust_manager.t27 +++ b/specs/trust_manager.t27 @@ -58,27 +58,41 @@ module trust_manager { return (rel & 0xFF); } - // 8-node trust storage + // 8-node trust storage: one 8-bit lane per slot, slot 0 in the high byte. + // Each argument is a bare trust score value in 0..=100 - a + // get_trust_score_value result, NOT a packed create_trust_score word. A + // 32-bit packed word does not survive an 8-bit lane: only its low byte, + // the negative-interaction count, would reach the slot. + // The t8 slots are combined with bitwise or '|'. A logical or '||' here + // would be a type error, and would also make t27c lower the whole chain + // as a boolean; every slot uses the same operator. fn create_trust_array(t0: u32, t1: u32, t2: u32, t3: u32, t4: u32, t5: u32, t6: u32, t7: u32) -> u64 { - return (((t0 as u64) << 56) | - ((t1 as u64) << 48) | - ((t2 as u64) << 40) | - ((t3 as u64) << 32) | - ((t4 as u64) << 24) || - ((t5 as u64) << 16) | - ((t6 as u64) << 8) | - (t7 as u64)); - } - + return ((((t0 as u64) & 0xFF) << 56) | + (((t1 as u64) & 0xFF) << 48) | + (((t2 as u64) & 0xFF) << 40) | + (((t3 as u64) & 0xFF) << 32) | + (((t4 as u64) & 0xFF) << 24) | + (((t5 as u64) & 0xFF) << 16) | + (((t6 as u64) & 0xFF) << 8) | + ((t7 as u64) & 0xFF)); + } + + // Returns the bare 8-bit trust score value held in the slot, so the mask + // matches the lane width create_trust_array packs. A wider mask splices + // neighbouring slots into the result instead of isolating one. + // Every arm is a parenthesised condition with an explicit return, and the + // braces are balanced. An unbalanced brace here does not raise an error: + // t27c's parser recovers by discarding statements to the next boundary, + // which silently deletes every function declared after this one. fn get_trust_score(array: u64, index: u32) -> u32 { - if (index == 0) { return ((array >> 56) & 0xFFFFFFFF) as u32; } - if (index == 1) { return ((array >> 48) & 0xFFFFFFFF) as u32; } - if (index == 2) { return ((array >> 40) & 0xFFFFFFFF) as u32; } - if (index == 3) { return ((array >> 32) & 0xFFFFFFFF) as u32; } - if (index == 4) { return ((array >> 24) & 0xFFFFFFFF) as u32; } - if (index == 5) { return ((array >> 16) & 0xFFFFFFFF) as u32; } - if (index == 6) { { return ((array >> 8) & 0xFFFFFFFF) as u32; } - return (array & 0xFFFFFFFF) as u32; + if (index == 0) { return ((array >> 56) & 0xFF) as u32; } + if (index == 1) { return ((array >> 48) & 0xFF) as u32; } + if (index == 2) { return ((array >> 40) & 0xFF) as u32; } + if (index == 3) { return ((array >> 32) & 0xFF) as u32; } + if (index == 4) { return ((array >> 24) & 0xFF) as u32; } + if (index == 5) { return ((array >> 16) & 0xFF) as u32; } + if (index == 6) { return ((array >> 8) & 0xFF) as u32; } + return (array & 0xFF) as u32; } // Calculate trust score from interactions @@ -122,50 +136,53 @@ module trust_manager { } // Find most trusted node + // get_trust_score already yields the bare score value for a slot, so it is + // used directly. Wrapping it in get_trust_score_value would shift a value + // that is at most 8 bits wide right by 16 and always read zero. fn find_most_trusted(trust_array: u64) -> u32 { let highest_score = 0; let most_trusted = 0xFF; - - if (get_trust_score_value(get_trust_score(trust_array, 0)) > highest_score) { - highest_score = get_trust_score_value(get_trust_score(trust_array, 0)); + + if (get_trust_score(trust_array, 0) > highest_score) { + highest_score = get_trust_score(trust_array, 0); most_trusted = 0; } - - if (get_trust_score_value(get_trust_score(trust_array, 1)) > highest_score) { - highest_score = get_trust_score_value(get_trust_score(trust_array, 1)); + + if (get_trust_score(trust_array, 1) > highest_score) { + highest_score = get_trust_score(trust_array, 1); most_trusted = 1; } - - if (get_trust_score_value(get_trust_score(trust_array, 2)) > highest_score) { - highest_score = get_trust_score_value(get_trust_score(trust_array, 2)); + + if (get_trust_score(trust_array, 2) > highest_score) { + highest_score = get_trust_score(trust_array, 2); most_trusted = 2; } - - if (get_trust_score_value(get_trust_score(trust_array, 3)) > highest_score) { - highest_score = get_trust_score_value(get_trust_score(trust_array, 3)); + + if (get_trust_score(trust_array, 3) > highest_score) { + highest_score = get_trust_score(trust_array, 3); most_trusted = 3; } - - if (get_trust_score_value(get_trust_score(trust_array, 4)) > highest_score) { - highest_score = get_trust_score_value(get_trust_score(trust_array, 4)); + + if (get_trust_score(trust_array, 4) > highest_score) { + highest_score = get_trust_score(trust_array, 4); most_trusted = 4; } - - if (get_tr_score_value(get_trust_score(trust_array, 5)) > highest_score) { - highest_score = get_trust_score_value(get_trust_score(trust_array, 5)); + + if (get_trust_score(trust_array, 5) > highest_score) { + highest_score = get_trust_score(trust_array, 5); most_trusted = 5; } - - if (get_trust_score_value(get_trust_score(trust_array, 6)) > highest_score) { - highest_score = get_trust_score_value(get_trust_score(trust_array, 6)); + + if (get_trust_score(trust_array, 6) > highest_score) { + highest_score = get_trust_score(trust_array, 6); most_trusted = 6; } - - if (get_trust_score_value(get_trust_score(trust_array, 7)) > highest_score) { - highest_score = get_trust_score_value(get_trust_score(trust_array, 7)); + + if (get_trust_score(trust_array, 7) > highest_score) { + highest_score = get_trust_score(trust_array, 7); most_trusted = 7; } - + return most_trusted; } @@ -174,7 +191,7 @@ module trust_manager { if (node_index >= MAX_NODES) { return false; } let score = get_trust_score(trust_array, node_index); - return (get_trust_score_value(score) >= min_trust); + return (score >= min_trust); } // Penalize node for bad behavior @@ -210,11 +227,13 @@ module trust_manager { } test create_trust_relationship_basic { - rel = create_trust_relationship(1, 2, 80, 1000); + // verified occupies the low 8 bits, so the witness must fit in 8 bits. + // The former value 1000 was stored as 1000 & 0xFF = 232. + rel = create_trust_relationship(1, 2, 80, 200); assert(get_trust_source(rel) == 1, "source"); assert(get_trust_destination(rel) == 2, "destination"); assert(get_trust_level(rel) == 80, "trust level"); - assert(get_trust_verified(rel) == 1000, "verified time"); + assert(get_trust_verified(rel) == 200, "verified time"); } test calculate_trust_score_balanced { @@ -237,9 +256,13 @@ module trust_manager { } test update_trust_score_increases { + // 5 positive and 5 negative give a starting score of 50; adding 3 + // positive and 1 negative gives 8 of 14, i.e. 57. The bound is the + // starting score, mirroring update_trust_score_decreases below. The + // former bound of 60 was unreachable from these interaction counts. current = create_trust_score(5, 50, 5, 5); updated = update_trust_score(current, 3, 1); - assert(get_trust_score_value(updated) >= 60, "trust increased"); + assert(get_trust_score_value(updated) > 50, "trust increased"); } test update_trust_score_decreases { @@ -269,36 +292,23 @@ module trust_manager { } test find_most_trusted_middle { - array = create_trust_array( - create_trust_score(1, 60, 6, 4), - create_trust_score(2, 90, 9, 1), // Most trusted - create_trust_score(3, 45, 5, 5), - create_trust_score(4, 75, 8, 2), - 0, 0, 0, 0 - ); - assert(find_most_trusted(array) == 1, "node 1 most trusted"); + // One bare trust score value per slot. Passing a packed + // create_trust_score word here would store only its low byte. + // s0 s1 s2 s3 + // ^^ most trusted + array = create_trust_array(60, 90, 45, 75, 0, 0, 0, 0); + assert(find_most_trusted(array) == 1, "slot 1 most trusted"); } test should_route_via_node_true { - array = create_trust_array( - create_trust_score(1, 80, 8, 2), - create_trust_score(2, 60, 6, 4), - create_trust_score(3, 75, 7, 3), - create_trust_score(4, 70, 7, 3), - 0, 0, 0, 0 - ); - assert(should_route_via_node(array, 0, 70) == true, "can route via node 0"); + // One bare trust score value per slot, as in find_most_trusted_middle. + array = create_trust_array(80, 60, 75, 70, 0, 0, 0, 0); + assert(should_route_via_node(array, 0, 70) == true, "can route via slot 0"); } test should_route_via_node_false { - array = create_trust_array( - create_trust_score(1, 80, 8, 2), - create_trust_score(2, 60, 6, 4), - create_trust_score(3, 75, 7, 3), - create_trust_score(4, 70, 7, 3), - 0, 0, 0, 0 - ); - assert(should_route_via_node(array, 2, 90) == false, "cannot route via node 2"); + array = create_trust_array(80, 60, 75, 70, 0, 0, 0, 0); + assert(should_route_via_node(array, 2, 90) == false, "cannot route via slot 2"); } test penalize_node_reduces_trust { @@ -313,4 +323,3 @@ module trust_manager { assert(get_trust_score_value(rewarded) > 70, "trust increased"); } } -} diff --git a/src/bin/trios_meshd.rs b/src/bin/trios_meshd.rs index 879dc58b..6bec4f1e 100644 --- a/src/bin/trios_meshd.rs +++ b/src/bin/trios_meshd.rs @@ -1,13 +1,13 @@ -//! trios-meshd — minimal TRI-NET mesh daemon over a UDP transport. +//! trios-meshd - minimal TRI-NET mesh daemon over a UDP transport. //! //! Runs on each node. Uses UDP-over-Ethernet as the link transport (stand-in //! for the 5.8 GHz radio, which swaps in later as a different `Transport`), so -//! the full mesh stack — per-hop ChaCha20-Poly1305 crypto, ETX routing from -//! HELLO beacons, and multi-hop forwarding — can be validated on real hardware +//! the full mesh stack - per-hop ChaCha20-Poly1305 crypto, ETX routing from +//! HELLO beacons, and multi-hop forwarding - can be validated on real hardware //! WITHOUT radiating anything (legally clean for development). //! //! Demo keys are derived deterministically from node id (a pre-shared-key mesh, -//! an allow-list); real ephemeral auth is the Noise-XX path (tri-net#… / B01). +//! an allow-list); real ephemeral auth is the Noise-XX path (tri-net#... / B01). //! //! Config file (one directive per line): //! id 11 @@ -19,6 +19,7 @@ use sha2::{Digest, Sha256}; use std::collections::{HashMap, HashSet}; use std::io; use std::net::{SocketAddr, UdpSocket}; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; @@ -41,6 +42,21 @@ fn seed_for(id: NodeId) -> [u8; 32] { h.finalize().into() } +/// Deterministic shared HELLO session key for the demo PSK mesh. +/// All nodes must use the same set of IDs for this to be consistent; in a real +/// deployment the key is derived from the per-peer Noise-XX session secret. +fn demo_hello_session_key(peer_ids: &[NodeId]) -> [u8; 32] { + let mut ids: Vec = peer_ids.to_vec(); + ids.sort_unstable(); + ids.dedup(); + let mut h = Sha256::new(); + h.update(b"trios-mesh/demo/v1/hello-key"); + for id in ids { + h.update(id.to_le_bytes()); + } + h.finalize().into() +} + /// Gateway-side internet fetch (M4): GET the caller's public IP. Runs only on /// the node that actually has an uplink; the result travels back over the mesh. fn fetch_public_ip() -> String { @@ -83,26 +99,44 @@ struct Cfg { peers: Vec<(NodeId, SocketAddr)>, } -fn parse_cfg(text: &str) -> Cfg { +fn parse_cfg(text: &str) -> Result { let mut id = 0u32; let mut listen = None; let mut peers = Vec::new(); - for line in text.lines() { + for (line_no, line) in text.lines().enumerate() { let f: Vec<&str> = line.split_whitespace().collect(); match f.as_slice() { - ["id", v] => id = v.parse().expect("id"), - ["listen", a] => listen = Some(a.parse().expect("listen addr")), + ["id", v] => { + id = v + .parse() + .map_err(|e| format!("line {}: invalid id '{}': {}", line_no + 1, v, e))?; + } + ["listen", a] => { + listen = Some(a.parse().map_err(|e| { + format!("line {}: invalid listen addr '{}': {}", line_no + 1, a, e) + })?); + } ["peer", pid, a] => { - peers.push((pid.parse().expect("peer id"), a.parse().expect("peer addr"))) + let pid = pid.parse().map_err(|e| { + format!("line {}: invalid peer id '{}': {}", line_no + 1, pid, e) + })?; + let addr = a.parse().map_err(|e| { + format!("line {}: invalid peer addr '{}': {}", line_no + 1, a, e) + })?; + peers.push((pid, addr)); } + [] => {} _ => {} } } - Cfg { + if id == 0 { + return Err("config missing required 'id '".into()); + } + Ok(Cfg { id, listen: listen.unwrap_or_else(|| SocketAddr::from(([0, 0, 0, 0], 5000))), peers, - } + }) } #[derive(Default)] @@ -111,15 +145,49 @@ struct RxShared { they_heard: HashMap, } +/// Default path for the M5 simulated-link-failure drop set. +/// Uses `TRIOS_MESH_DROP` if set, otherwise `.trinity/run/mesh.drop` under the +/// current working directory (the project root when run from trios/). +fn mesh_drop_path() -> PathBuf { + std::env::var("TRIOS_MESH_DROP") + .map(PathBuf::from) + .unwrap_or_else(|_| { + std::env::current_dir() + .unwrap_or_else(|_| PathBuf::from(".")) + .join(".trinity/run/mesh.drop") + }) +} + +fn read_drop_set(path: &Path) -> HashSet { + std::fs::read_to_string(path) + .ok() + .map(|s| { + s.split_whitespace() + .filter_map(|x| x.parse().ok()) + .collect() + }) + .unwrap_or_default() +} + fn main() { - let path = std::env::args() - .nth(1) - .expect("usage: trios-meshd "); - let cfg = parse_cfg(&std::fs::read_to_string(&path).expect("read config")); + if let Err(e) = run() { + eprintln!("[meshd] FATAL: {e}"); + std::process::exit(1); + } +} + +fn run() -> Result<(), String> { + let path = std::env::args().nth(1).ok_or("usage: trios-meshd ")?; + let text = std::fs::read_to_string(&path) + .map_err(|e| format!("cannot read config '{}': {}", path, e))?; + let cfg = parse_cfg(&text)?; let me = cfg.id; let my_key = StaticKey::from_seed(seed_for(me)); - let sock = Arc::new(UdpSocket::bind(cfg.listen).expect("bind")); + let sock = Arc::new( + UdpSocket::bind(cfg.listen) + .map_err(|e| format!("cannot bind to {}: {}", cfg.listen, e))?, + ); let mut router = MeshRouter::new(me, ETX_WINDOW); let mut peer_ids: Vec = Vec::new(); // Key by full SocketAddr (IP + port) so that loopback smokes with @@ -130,7 +198,9 @@ fn main() { let mut addr_to_id: HashMap = HashMap::new(); for (pid, addr) in &cfg.peers { let peer_pub = StaticKey::from_seed(seed_for(*pid)).public(); - let session = my_key.session_with(&peer_pub, me < *pid); + let session = my_key + .session_with(&peer_pub, me < *pid) + .map_err(|_| format!("session derivation failed for peer {}", pid))?; router.add_link( *pid, session, @@ -144,7 +214,10 @@ fn main() { } let router = Arc::new(Mutex::new(router)); let rx = Arc::new(Mutex::new(RxShared::default())); - // Peers whose link is simulated-failed (ids in /tmp/mesh.drop) — for M5 demo. + // E2.2 - deterministic demo HELLO MAC key shared by all nodes in this PSK mesh. + // In a real deployment this is replaced by the per-peer Noise-XX session secret. + let demo_hello_key = demo_hello_session_key(&peer_ids); + // Peers whose link is simulated-failed (ids in .trinity/run/mesh.drop) - for M5 demo. let dropped: Arc>> = Arc::new(Mutex::new(HashSet::new())); let watch: Option = std::env::var("TRIOS_WATCH") .ok() @@ -152,10 +225,11 @@ fn main() { // M4: this node has a real internet uplink and serves FETCH requests. let gateway = std::env::var("TRIOS_GATEWAY").is_ok(); let started = Instant::now(); - println!("[meshd] node {me} on {} — peers {peer_ids:?}", cfg.listen); + println!("[meshd] node {me} on {} - peers {peer_ids:?}", cfg.listen); // Central RX: dispatch every datagram through the router. { + let demo_hello_key = demo_hello_key; let (sock, router, rx, addr_to_id, dropped) = ( sock.clone(), router.clone(), @@ -174,16 +248,33 @@ fn main() { Some(f) => *f, None => continue, }; - if dropped.lock().unwrap().contains(&from) { + if dropped + .lock() + .unwrap_or_else(|p| p.into_inner()) + .contains(&from) + { continue; // simulated link failure: ignore this neighbor } - let deliv = router.lock().unwrap().handle_frame(from, &buf[..n]); + let deliv = router + .lock() + .unwrap_or_else(|p| p.into_inner()) + .handle_frame(from, &buf[..n]); match deliv { Delivery::Local(p) if p.first() == Some(&HELLO_TYPE) => { if let Some(h) = Hello::parse(&p[1..]) { - let mut r = rx.lock().unwrap(); - r.seen.insert(from); - r.they_heard.insert(from, h.reports_hearing(me)); + // E2.2 / E2.3 - authenticate and freshness-check the + // beacon before accepting it into routing state. + if h.src != from { + println!("[meshd] HELLO src mismatch {h.src} != {from} from {src}"); + } else if !h.verify_mac(&demo_hello_key) { + println!("[meshd] HELLO MAC failed from {from}"); + } else if !h.is_fresh() { + println!("[meshd] stale HELLO from {from} ts={}", h.ts); + } else { + let mut r = rx.lock().unwrap_or_else(|p| p.into_inner()); + r.seen.insert(from); + r.they_heard.insert(from, h.reports_hearing(me)); + } } } Delivery::Local(p) if p.first() == Some(&DATA_TYPE) => { @@ -202,13 +293,16 @@ fn main() { let ip = fetch_public_ip(); let mut resp = vec![FETCH_RESP]; resp.extend_from_slice(ip.as_bytes()); - let d = router.lock().unwrap().send_ip(origin, &resp); + let d = router + .lock() + .unwrap_or_else(|p| p.into_inner()) + .send_ip(origin, &resp); println!( "[meshd] gateway fetched \"{ip}\" -> reply to {origin}: {d:?}" ); }); } - // M4: the gateway's reply — internet reached us over the mesh. + // M4: the gateway's reply - internet reached us over the mesh. Delivery::Local(p) if p.first() == Some(&FETCH_RESP) => { println!( "[meshd] INTERNET-VIA-MESH: {}", @@ -225,14 +319,19 @@ fn main() { // Optional one-shot test packet: TRIOS_SEND="dst:message". if let Ok(spec) = std::env::var("TRIOS_SEND") { if let Some((d, m)) = spec.split_once(':') { - let dst: NodeId = d.parse().expect("send dst"); + let dst: NodeId = d + .parse() + .map_err(|e| format!("TRIOS_SEND has invalid dst '{d}': {e}"))?; let msg = m.as_bytes().to_vec(); let router = router.clone(); thread::spawn(move || { thread::sleep(Duration::from_secs(4)); let mut payload = vec![DATA_TYPE]; payload.extend_from_slice(&msg); - let d = router.lock().unwrap().send_ip(dst, &payload); + let d = router + .lock() + .unwrap_or_else(|p| p.into_inner()) + .send_ip(dst, &payload); println!("[meshd] TX test -> {dst}: {d:?}"); }); } @@ -247,12 +346,21 @@ fn main() { thread::sleep(Duration::from_secs(5)); let mut req = vec![FETCH_REQ]; req.extend_from_slice(&me.to_le_bytes()); - let d = router.lock().unwrap().send_ip(gw, &req); + let d = router + .lock() + .unwrap_or_else(|p| p.into_inner()) + .send_ip(gw, &req); println!("[meshd] FETCH internet via mesh -> gateway {gw}: {d:?}"); }); } } + let drop_path = mesh_drop_path(); + // Ensure parent dir exists so the drop file can be created by an operator. + if let Some(parent) = drop_path.parent() { + let _ = std::fs::create_dir_all(parent); + } + // Ticker: measure ETX for the interval, beacon HELLO, print status. let mut seq = 0u32; let mut tick = 0u64; @@ -262,7 +370,7 @@ fn main() { seq += 1; tick += 1; let (seen, they) = { - let mut r = rx.lock().unwrap(); + let mut r = rx.lock().unwrap_or_else(|p| p.into_inner()); (std::mem::take(&mut r.seen), r.they_heard.clone()) }; let heard: Vec = { @@ -270,18 +378,11 @@ fn main() { v.sort(); v }; - // Refresh the simulated link-failure set from /tmp/mesh.drop (M5 control). - let dset: HashSet = std::fs::read_to_string("/tmp/mesh.drop") - .ok() - .map(|s| { - s.split_whitespace() - .filter_map(|x| x.parse().ok()) - .collect() - }) - .unwrap_or_default(); - *dropped.lock().unwrap() = dset.clone(); + // Refresh the simulated link-failure set from .trinity/run/mesh.drop (M5 control). + let dset: HashSet = read_drop_set(&drop_path); + *dropped.lock().unwrap_or_else(|p| p.into_inner()) = dset.clone(); - let mut rt = router.lock().unwrap(); + let mut rt = router.lock().unwrap_or_else(|p| p.into_inner()); for pid in &peer_ids { let alive = !dset.contains(pid); let heard = seen.contains(pid) && alive; @@ -296,10 +397,14 @@ fn main() { } } } - // E2.2 — Use authenticated HELLO with MAC - // TODO: derive mac_key from session keys (E2.2 complete implementation) - let mac_key = None; // Will be derived from per-peer session keys - let hello = Hello::authenticated(me, seq, heard, &mac_key); + // Build and send an authenticated HELLO using the pre-derived demo key. + let hello = match Hello::authenticated(me, seq, heard, &demo_hello_key) { + Ok(h) => h, + Err(e) => { + println!("[meshd] HELLO auth failed for node {me}: {e:?}"); + continue; + } + }; let mut pay = vec![HELLO_TYPE]; pay.extend_from_slice(&hello.to_bytes()); for pid in &peer_ids { diff --git a/src/crypto.rs b/src/crypto.rs index e568b9eb..dfdebccd 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,4 +1,4 @@ -//! M1 crypto core: X25519 handshake → HKDF session key → ChaCha20-Poly1305 AEAD +//! M1 crypto core: X25519 handshake -> HKDF session key -> ChaCha20-Poly1305 AEAD //! with a directional 96-bit nonce and a 64-frame sliding replay window. //! //! Adds a symmetric **HKDF ratchet** (B10 / tri-net#10): the session periodically @@ -9,7 +9,7 @@ //! current epoch's key, not the whole session. All key material (`EphemeralSecret`, //! HKDF output, chain key) is wiped on rekey and on drop. //! -//! The ratchet is driven purely by frame count here — `seal` auto-ratchets at +//! The ratchet is driven purely by frame count here - `seal` auto-ratchets at //! [`REKEY_EVERY_FRAMES`] and refuses to reuse a nonce past [`REKEY_HARD_CAP`] //! (returning [`MeshError::RekeyRequired`]). Time-based rekeying and the //! daemon-side handling of `RekeyRequired` are deferred to the M2 run loop @@ -23,9 +23,10 @@ use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; use hkdf::Hkdf; use rand_core::OsRng; use sha2::Sha256; -use x25519_dalek::{EphemeralSecret, PublicKey, StaticSecret}; use zeroize::Zeroizing; +pub use x25519_dalek::{EphemeralSecret, PublicKey, StaticSecret}; + /// HKDF context so keys derived here never collide with another protocol. const HKDF_SALT: &[u8] = b"trios-mesh/v1/session"; /// Info label for the initial (epoch-0) AEAD key derived from the DH secret. @@ -40,8 +41,8 @@ pub const REKEY_EVERY_FRAMES: u64 = 1 << 20; // ~1.05M frames per epoch /// Absolute per-key ceiling. Sealing at this counter fails rather than reusing a /// nonce. Chosen so `REKEY_HARD_CAP * MAX_FRAME` stays well below ChaCha20's -/// 2^32-block limit — see the compile-time assertion below. -pub const REKEY_HARD_CAP: u64 = 1 << 24; // 16.7M frames — hard nonce-reuse guard +/// 2^32-block limit - see the compile-time assertion below. +pub const REKEY_HARD_CAP: u64 = 1 << 24; // 16.7M frames - hard nonce-reuse guard /// Largest single payload the mesh frames (matches the single-carrier modem cap). /// Used only to bound the per-key block budget at compile time. @@ -56,6 +57,19 @@ const _: () = assert!(REKEY_HARD_CAP * 4 < (1u64 << 32)); // Counter stays within the 7-byte (56-bit) nonce counter field. const _: () = assert!(REKEY_HARD_CAP < (1u64 << 56)); +/// HKDF-Expand into a 32-byte buffer. SHA256 can always expand to 32 bytes, +/// so an error here is a code invariant violation, not an attacker action. +/// Map it to `MeshError::CryptoInternal` so the daemon never panics. +fn hkdf_expand_32(hk: &Hkdf, info: &[u8], out: &mut [u8; 32]) -> Result<(), MeshError> { + hk.expand(info, out).map_err(|_| MeshError::CryptoInternal) +} + +/// Build an HKDF from a PRK. SHA256 accepts any non-empty PRK, so a 32-byte +/// key is always valid. Map unexpected failure to `CryptoInternal`. +fn hkdf_from_prk(prk: &[u8]) -> Result, MeshError> { + Hkdf::::from_prk(prk).map_err(|_| MeshError::CryptoInternal) +} + #[derive(Debug, PartialEq, Eq)] pub enum MeshError { /// AEAD tag verification failed (tampered, wrong key, or wrong epoch). @@ -67,6 +81,9 @@ pub enum MeshError { /// Per-key hard cap reached: the caller must `ratchet()` before sealing more, /// rather than risk a nonce reuse. Handled by the M2 loop (tri-net#11). RekeyRequired, + /// Internal crypto primitive failed on an input that should be valid. + /// Treat as an auth-equivalent failure; do not crash the daemon. + CryptoInternal, } /// One side of an ephemeral X25519 handshake. `EphemeralSecret` zeroizes on drop @@ -83,8 +100,9 @@ pub struct Handshake { /// XX pattern flow: /// ```text /// Initiator Responder -/// e ← e, ee, s, es -/// s, se ← +/// e ----------------> +/// <---------------- e, ee, s, es +/// s, se ----------------> /// ``` /// After `complete()`, both parties have derived the same session key and /// verified each other's static keys. @@ -129,26 +147,34 @@ impl NoiseXX { /// Complete as initiator: receive responder's message, derive session. /// Input: (responder_ephemeral_pub, responder_static_pub) - pub fn complete_initiator(self, peer_ephemeral: PublicKey, peer_static: PublicKey) -> Session { + pub fn complete_initiator( + self, + peer_ephemeral: PublicKey, + peer_static: PublicKey, + ) -> Result { // SIMPLIFIED Noise-XX: Use only ee (ephemeral-ephemeral) + ss (static-static) // Proper Noise-XX would use ee, es, se but that requires multiple ephemeral DH ops - // ee = ephemeral × peer_ephemeral + // ee = ephemeral x peer_ephemeral let ee = self.ephemeral.diffie_hellman(&peer_ephemeral); let ee_bytes = *ee.as_bytes(); - // ss = static × peer_static (both sides compute this, gets same result) + // ss = static x peer_static (both sides compute this, gets same result) let ss = self.static_secret.diffie_hellman(&peer_static); let ss_bytes = *ss.as_bytes(); // Combine ee + ss (both sides get same result) - let combined = combine_dh_shares(&ee_bytes, &ss_bytes, &ss_bytes); + let combined = combine_dh_shares(&ee_bytes, &ss_bytes, &ss_bytes)?; Session::from_shared(&combined, true) } /// Complete as responder: receive initiator's static, derive session. /// Input: (initiator_ephemeral_pub, initiator_static_pub) - pub fn complete_responder(self, peer_ephemeral: PublicKey, peer_static: PublicKey) -> Session { + pub fn complete_responder( + self, + peer_ephemeral: PublicKey, + peer_static: PublicKey, + ) -> Result { // Same as initiator: ee + ss (both sides compute same) let ee = self.ephemeral.diffie_hellman(&peer_ephemeral); let ee_bytes = *ee.as_bytes(); @@ -156,13 +182,17 @@ impl NoiseXX { let ss = self.static_secret.diffie_hellman(&peer_static); let ss_bytes = *ss.as_bytes(); - let combined = combine_dh_shares(&ee_bytes, &ss_bytes, &ss_bytes); + let combined = combine_dh_shares(&ee_bytes, &ss_bytes, &ss_bytes)?; Session::from_shared(&combined, false) } } /// Combine three X25519 DH outputs (ee, es, se) into a single 32-byte key using HKDF. -fn combine_dh_shares(ee_bytes: &[u8; 32], es_bytes: &[u8; 32], se_bytes: &[u8; 32]) -> [u8; 32] { +fn combine_dh_shares( + ee_bytes: &[u8; 32], + es_bytes: &[u8; 32], + se_bytes: &[u8; 32], +) -> Result<[u8; 32], MeshError> { let mut combined = [0u8; 96]; combined[0..32].copy_from_slice(ee_bytes); combined[32..64].copy_from_slice(es_bytes); @@ -171,17 +201,16 @@ fn combine_dh_shares(ee_bytes: &[u8; 32], es_bytes: &[u8; 32], se_bytes: &[u8; 3 // HKDF to mix the three shares let hk = Hkdf::::new(Some(HKDF_SALT), &combined); let mut output = [0u8; 32]; - hk.expand(b"noise-xx-combine", &mut output) - .expect("32 bytes is a valid HKDF-SHA256 output length"); - output + hkdf_expand_32(&hk, b"noise-xx-combine", &mut output)?; + Ok(output) } -/// Allow-list of trusted NodeId → PublicKey mappings. Used to authenticate +/// Allow-list of trusted NodeId -> PublicKey mappings. Used to authenticate /// peers in Noise-XX handshakes (E1.2). Only peers with static keys in this /// list are allowed to establish sessions. #[derive(Debug, Clone)] pub struct AllowList { - /// Map of node_id → trusted public key + /// Map of node_id -> trusted public key trusted: std::collections::HashMap, } @@ -249,7 +278,7 @@ impl Handshake { /// never overlap (initiator sends with direction byte 0, responder with 1). /// `self` is consumed, so the `EphemeralSecret` is dropped (and zeroized) /// as soon as the shared secret is derived. - pub fn complete(self, peer: &PublicKey, initiator: bool) -> Session { + pub fn complete(self, peer: &PublicKey, initiator: bool) -> Result { let shared = self.secret.diffie_hellman(peer); Session::from_shared(shared.as_bytes(), initiator) } @@ -270,10 +299,28 @@ pub struct StaticKey(StaticSecret); impl StaticKey { /// Deterministic keypair from a 32-byte seed. + /// + /// Useful only for tests and deterministic benchmarks; production identities + /// must be loaded from a secure source or generated by `StaticKey::generate`. pub fn from_seed(seed: [u8; 32]) -> Self { StaticKey(StaticSecret::from(seed)) } + /// Wrap an existing X25519 static secret. + pub fn from_secret(secret: StaticSecret) -> Self { + StaticKey(secret) + } + + /// Generate a fresh, unpredictable identity key from the OS CSPRNG. + pub fn generate() -> Self { + StaticKey(StaticSecret::random_from_rng(OsRng)) + } + + /// The raw 32-byte secret. Handle with care: this is the long-term identity. + pub fn secret_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + /// This key's public half (share with peers). pub fn public(&self) -> PublicKey { PublicKey::from(&self.0) @@ -281,7 +328,7 @@ impl StaticKey { /// Derive the session to a peer whose public key is already trusted. /// `initiator` must differ between the two peers (e.g. lower node id = true). - pub fn session_with(&self, peer: &PublicKey, initiator: bool) -> Session { + pub fn session_with(&self, peer: &PublicKey, initiator: bool) -> Result { let shared = self.0.diffie_hellman(peer); Session::from_shared(shared.as_bytes(), initiator) } @@ -318,23 +365,22 @@ impl std::fmt::Debug for Session { } impl Session { - fn from_shared(shared: &[u8; 32], initiator: bool) -> Self { + fn from_shared(shared: &[u8; 32], initiator: bool) -> Result { // The DH output seeds the ratchet chain; the epoch-0 AEAD key is one // HKDF-Expand off it. Both peers derive the identical chain from the // symmetric X25519 secret, so their epochs stay in lock-step. let hk = Hkdf::::new(Some(HKDF_SALT), shared); let mut chain = Zeroizing::new([0u8; 32]); - hk.expand(b"ratchet-chain", chain.as_mut()) - .expect("32 bytes is a valid HKDF-SHA256 output length"); - let cipher = derive_cipher(&chain, HKDF_INFO); - Self { + hkdf_expand_32(&hk, b"ratchet-chain", &mut chain)?; + let cipher = derive_cipher(&chain, HKDF_INFO)?; + Ok(Self { cipher, chain_key: chain, epoch: 0, tx_dir: if initiator { 0 } else { 1 }, tx_counter: 0, rx: ReplayWindow::new(), - } + }) } /// Current ratchet epoch (starts at 0). Exposed for tests and future M2 @@ -346,24 +392,23 @@ impl Session { /// Advance the symmetric ratchet: derive the next chain key and AEAD key, /// bump the epoch, reset the counter and replay window, and zeroize the old /// chain key. Both peers must ratchet in lock-step (same trigger) so their - /// epochs — and therefore their nonces — stay aligned. + /// epochs - and therefore their nonces - stay aligned. /// /// The old key is unrecoverable after this call, giving forward secrecy: /// capturing the node now leaks only the new epoch's key. - pub fn ratchet(&mut self) { + pub fn ratchet(&mut self) -> Result<(), MeshError> { // key_{i+1} = HKDF-Expand(chain_i, "aead-rekey"); chain_{i+1} likewise // off a distinct label. Zeroizing wraps the new chain and drops (wipes) // the old one on assignment. - let hk = Hkdf::::from_prk(self.chain_key.as_ref()) - .expect("32-byte chain key is a valid HKDF-SHA256 PRK"); + let hk = hkdf_from_prk(self.chain_key.as_ref())?; let mut next_chain = Zeroizing::new([0u8; 32]); - hk.expand(b"ratchet-chain", next_chain.as_mut()) - .expect("32 bytes is a valid HKDF-SHA256 output length"); - self.cipher = derive_cipher(&next_chain, HKDF_INFO_RATCHET); + hkdf_expand_32(&hk, b"ratchet-chain", &mut next_chain)?; + self.cipher = derive_cipher(&next_chain, HKDF_INFO_RATCHET)?; self.chain_key = next_chain; // old chain key dropped -> zeroized self.epoch = self.epoch.wrapping_add(1); self.tx_counter = 0; self.rx = ReplayWindow::new(); + Ok(()) } /// Seal `plaintext` with associated data `aad`. @@ -383,7 +428,7 @@ impl Session { } // Routine forward-secrecy ratchet: bounded per-epoch data budget. if self.tx_counter >= REKEY_EVERY_FRAMES { - self.ratchet(); + self.ratchet()?; } let epoch = self.epoch; let ctr = self.tx_counter; @@ -398,7 +443,7 @@ impl Session { aad, }, ) - .expect("ChaCha20-Poly1305 encryption is infallible for valid inputs"); + .map_err(|_| MeshError::CryptoInternal)?; let mut out = Vec::with_capacity(12 + ct.len()); out.extend_from_slice(&epoch.to_be_bytes()); out.extend_from_slice(&ctr.to_be_bytes()); @@ -411,13 +456,13 @@ impl Session { /// /// The epoch travels in the frame prefix and is folded into the nonce, so a /// frame from a different epoch decrypts under a different nonce and fails - /// the tag as [`MeshError::Auth`] — cross-epoch replays cannot pass. + /// the tag as [`MeshError::Auth`] - cross-epoch replays cannot pass. pub fn open(&mut self, aad: &[u8], frame: &[u8]) -> Result, MeshError> { if frame.len() < 12 { return Err(MeshError::ShortFrame); } - let epoch = u32::from_be_bytes(frame[..4].try_into().expect("4-byte slice")); - let ctr = u64::from_be_bytes(frame[4..12].try_into().expect("8-byte slice")); + let epoch = read_u32_be(frame).ok_or(MeshError::ShortFrame)?; + let ctr = read_u64_be(&frame[4..]).ok_or(MeshError::ShortFrame)?; // The peer's TX direction is the opposite of ours. let rx_dir = 1 - self.tx_dir; let nonce = make_nonce(rx_dir, epoch, ctr); @@ -443,16 +488,26 @@ impl Session { /// Derive a ChaCha20-Poly1305 cipher from a chain key under `info`, wiping the /// expanded key bytes immediately after the cipher captures them. -fn derive_cipher(chain: &Zeroizing<[u8; 32]>, info: &[u8]) -> ChaCha20Poly1305 { - let hk = Hkdf::::from_prk(chain.as_ref()) - .expect("32-byte chain key is a valid HKDF-SHA256 PRK"); +fn derive_cipher(chain: &Zeroizing<[u8; 32]>, info: &[u8]) -> Result { + let hk = hkdf_from_prk(chain.as_ref())?; let mut key = Zeroizing::new([0u8; 32]); - hk.expand(info, key.as_mut()) - .expect("32 bytes is a valid HKDF-SHA256 output length"); - ChaCha20Poly1305::new(Key::from_slice(key.as_ref())) + hkdf_expand_32(&hk, info, &mut key)?; + Ok(ChaCha20Poly1305::new(Key::from_slice(key.as_ref()))) // `key` (Zeroizing) is wiped here on drop. } +/// Read a big-endian u32 from the front of a slice that is known to be long +/// enough. Returns `None` only if the slice is shorter than 4 bytes. +fn read_u32_be(bytes: &[u8]) -> Option { + bytes.get(..4)?.try_into().ok().map(u32::from_be_bytes) +} + +/// Read a big-endian u64 from the front of a slice that is known to be long +/// enough. Returns `None` only if the slice is shorter than 8 bytes. +fn read_u64_be(bytes: &[u8]) -> Option { + bytes.get(..8)?.try_into().ok().map(u64::from_be_bytes) +} + /// 96-bit nonce = `[dir:1][epoch:4 BE][counter:7 BE]`. Unique per /// (direction, epoch, counter): the epoch prevents any nonce reuse across /// ratchet boundaries even though the counter resets to 0 each epoch. @@ -526,7 +581,10 @@ mod tests { let b = Handshake::new(); let a_pub = a.public; let b_pub = b.public; - (a.complete(&b_pub, true), b.complete(&a_pub, false)) + ( + a.complete(&b_pub, true).unwrap(), + b.complete(&a_pub, false).unwrap(), + ) } #[test] @@ -569,7 +627,7 @@ mod tests { let f0 = alice.seal(b"", b"0").unwrap(); let f1 = alice.seal(b"", b"1").unwrap(); let f2 = alice.seal(b"", b"2").unwrap(); - // Deliver 2, then 0, then 1 — all fresh, none replayed. + // Deliver 2, then 0, then 1 - all fresh, none replayed. assert_eq!(bob.open(b"", &f2).unwrap(), b"2"); assert_eq!(bob.open(b"", &f0).unwrap(), b"0"); assert_eq!(bob.open(b"", &f1).unwrap(), b"1"); @@ -589,7 +647,7 @@ mod tests { let (mut alice, _bob) = pair(); let (_alice2, mut bob2) = pair(); let frame = alice.seal(b"", b"cross").unwrap(); - // bob2's key is from a different handshake → must not decrypt. + // bob2's key is from a different handshake -> must not decrypt. assert_eq!(bob2.open(b"", &frame), Err(MeshError::Auth)); } @@ -600,11 +658,11 @@ mod tests { let (mut alice, mut bob) = pair(); // A frame sealed in epoch 0 must not open after the receiver ratchets. let e0 = alice.seal(b"", b"epoch0").unwrap(); - bob.ratchet(); + bob.ratchet().unwrap(); assert_eq!(bob.epoch(), 1); assert_eq!(bob.open(b"", &e0), Err(MeshError::Auth)); // Once the sender also ratchets, a fresh frame round-trips in epoch 1. - alice.ratchet(); + alice.ratchet().unwrap(); let e1 = alice.seal(b"", b"epoch1").unwrap(); assert_eq!(&e1[..4], &1u32.to_be_bytes()); // epoch tag on the wire assert_eq!(bob.open(b"", &e1).unwrap(), b"epoch1"); @@ -615,8 +673,8 @@ mod tests { let (mut alice, mut bob) = pair(); let old = alice.seal(b"", b"pre-ratchet").unwrap(); assert_eq!(bob.open(b"", &old).unwrap(), b"pre-ratchet"); - alice.ratchet(); - bob.ratchet(); + alice.ratchet().unwrap(); + bob.ratchet().unwrap(); // Counter restarts at 0 in the new epoch. let fresh = alice.seal(b"", b"post-ratchet").unwrap(); assert_eq!(&fresh[4..12], &0u64.to_be_bytes()); @@ -641,7 +699,7 @@ mod tests { assert_eq!(alice.epoch(), 1); assert_eq!(&first1[..4], &1u32.to_be_bytes()); // The receiver ratchets in lock-step and the frame still round-trips. - bob.ratchet(); + bob.ratchet().unwrap(); assert_eq!(bob.open(b"", &first1).unwrap(), b"first-of-epoch1"); } @@ -661,7 +719,7 @@ mod tests { #[test] fn key_material_is_zeroized() { use zeroize::Zeroize; - // Explicitly zeroizing a key buffer must wipe every byte to zero — this + // Explicitly zeroizing a key buffer must wipe every byte to zero - this // is exactly the guarantee `Zeroizing` invokes in its `Drop`. Tested on // an owned buffer (no `unsafe`, honoring the crate's forbid(unsafe_code)). let mut key = [0xABu8; 32]; @@ -695,8 +753,8 @@ mod tests { let b_static_pub = bob.static_public(); // Complete handshakes - let mut alice_sess = alice.complete_initiator(b_ephem, b_static_pub); - let mut bob_sess = bob.complete_responder(a_ephem, a_static_pub); + let mut alice_sess = alice.complete_initiator(b_ephem, b_static_pub).unwrap(); + let mut bob_sess = bob.complete_responder(a_ephem, a_static_pub).unwrap(); // They should derive the same session key let frame = alice_sess.seal(b"xx-test", b"authenticated mesh").unwrap(); @@ -724,10 +782,10 @@ mod tests { let mallory_pub = PublicKey::from(&mallory_static); // This should derive a different session key (authentication fails) - let mut alice_sess = alice.complete_initiator(b_ephem, mallory_pub); + let mut alice_sess = alice.complete_initiator(b_ephem, mallory_pub).unwrap(); // Bob correctly completed with Alice's static key - let mut bob_sess = bob.complete_responder(a_ephem, a_static_pub); + let mut bob_sess = bob.complete_responder(a_ephem, a_static_pub).unwrap(); // Frames won't decrypt - different session keys due to failed authentication let frame = alice_sess.seal(b"", b"fake message").unwrap(); @@ -755,17 +813,24 @@ mod tests { let mallory_alice = NoiseXX::new(mallory_static.clone(), false); let mallory_bob = NoiseXX::new(mallory_static, true); - // Alice ↔ Mallory handshake (Alice thinks it's Bob, but it's Mallory) - let mut alice_sess = alice.complete_initiator( - mallory_alice.ephemeral_public(), - mallory_alice.static_public(), - ); - let mut mal_sess_alice = mallory_alice.complete_responder(a_ephem, a_static_pub); - - // Bob ↔ Mallory handshake - let mut bob_sess = - bob.complete_responder(mallory_bob.ephemeral_public(), mallory_bob.static_public()); - let _mal_sess_bob = mallory_bob.complete_initiator(b_ephem, b_static_pub); + // Alice <-> Mallory handshake (Alice thinks it's Bob, but it's Mallory) + let mut alice_sess = alice + .complete_initiator( + mallory_alice.ephemeral_public(), + mallory_alice.static_public(), + ) + .unwrap(); + let mut mal_sess_alice = mallory_alice + .complete_responder(a_ephem, a_static_pub) + .unwrap(); + + // Bob <-> Mallory handshake + let mut bob_sess = bob + .complete_responder(mallory_bob.ephemeral_public(), mallory_bob.static_public()) + .unwrap(); + let _mal_sess_bob = mallory_bob + .complete_initiator(b_ephem, b_static_pub) + .unwrap(); // Alice sends message intended for Bob let frame = alice_sess.seal(b"", b"secret for bob").unwrap(); @@ -856,8 +921,8 @@ mod tests { let a_ephem = alice.ephemeral_public(); let b_ephem = bob.ephemeral_public(); - let mut alice_sess = alice.complete_initiator(b_ephem, b_pub); - let mut bob_sess = bob.complete_responder(a_ephem, a_pub); + let mut alice_sess = alice.complete_initiator(b_ephem, b_pub).unwrap(); + let mut bob_sess = bob.complete_responder(a_ephem, a_pub).unwrap(); let frame = alice_sess.seal(b"", b"verified").unwrap(); assert_eq!(bob_sess.open(b"", &frame).unwrap(), b"verified"); diff --git a/src/daemon.rs b/src/daemon.rs index b640f798..4972bcb8 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -189,8 +189,8 @@ mod tests { let (a_pub, b_pub) = (a.public, b.public); let mut na = Node::new(a_id, 16); let mut nb = Node::new(b_id, 16); - na.add_session(b_id, a.complete(&b_pub, true)); - nb.add_session(a_id, b.complete(&a_pub, false)); + na.add_session(b_id, a.complete(&b_pub, true).unwrap()); + nb.add_session(a_id, b.complete(&a_pub, false).unwrap()); (na, nb) } diff --git a/src/discovery.rs b/src/discovery.rs index 43e4824a..4b4d2c1e 100644 --- a/src/discovery.rs +++ b/src/discovery.rs @@ -1,34 +1,34 @@ //! HELLO beacons: each node periodically announces itself and the neighbors it //! currently hears, which lets peers compute the *forward* delivery ratio for ETX. //! -//! E2 — Authenticated HELLO: Each beacon now carries a timestamp and MAC to -//! prevent false-metric attacks (W2). Format: `[src:4][seq:4][ts:8][n:1][heard:n×4][mac:16]` +//! E2 - Authenticated HELLO: Each beacon now carries a timestamp and MAC to +//! prevent false-metric attacks (W2). Format: `[src:4][seq:4][ts:8][n:1][heard:nx4][mac:16]` +use crate::crypto::MeshError; use crate::routing::NodeId; -use chacha20poly1305::aead::{Aead, KeyInit, Payload}; -use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; +use hkdf::Hkdf; +use hmac::{Hmac, Mac}; +use sha2::Sha256; use std::time::{SystemTime, UNIX_EPOCH}; +use subtle::ConstantTimeEq; -/// E2.2 — MAC key for HELLO beacons (derived from session key) -const HELLO_MAC_KEY: [u8; 32] = [ - 0x74, 0x72, 0x69, 0x6f, 0x73, 0x2d, 0x6d, 0x65, 0x73, 0x68, 0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6f, - 0x2d, 0x6d, 0x61, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x2d, 0x76, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, -]; // "trios-mesh-hello-mac-key-v1" null-padded to 32 bytes +/// HKDF info label used to derive the HELLO MAC key from a session key. +const HELLO_MAC_INFO: &[u8] = b"hello-mac"; -/// E2.3 — Freshness threshold: reject beacons older than 2×HELLO_MS +/// E2.3 - Freshness threshold: reject beacons older than 2xHELLO_MS /// Assuming HELLO_MS = 300 ms, this is 600 ms const HELLO_FRESHNESS_MS: u64 = 600; -/// A HELLO beacon: `[src:4][seq:4][ts:8][n:1][heard: n × 4][mac:16]` (all big-endian). +/// A HELLO beacon: `[src:4][seq:4][ts:8][n:1][heard: n x 4][mac:16]` (all big-endian). #[derive(Clone, Debug, PartialEq, Eq)] pub struct Hello { pub src: NodeId, pub seq: u32, - /// E2.3 — Timestamp for freshness check + /// E2.3 - Timestamp for freshness check pub ts: u64, /// Neighbors this node currently hears (so they learn their forward link). pub heard: Vec, - /// E2.2 — MAC over (src, seq, ts, heard[]) + /// E2.2 - MAC over (src, seq, ts, heard[]) pub mac: [u8; 16], } @@ -43,7 +43,7 @@ impl Hello { } } - /// E2.3 — Get current timestamp as milliseconds since Unix epoch + /// E2.3 - Get current timestamp as milliseconds since Unix epoch pub fn now_ms() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) @@ -51,40 +51,49 @@ impl Hello { .as_millis() as u64 } - /// Create a beacon with automatic timestamp and MAC calculation (E2.2) - /// Uses a symmetric key for MAC (in production, derive from session key) + /// Create a beacon with automatic timestamp and MAC calculation (E2.2). + /// The HELLO MAC key is derived from `session_key` via HKDF; no session key + /// means no beacon can be authenticated, so this fails closed. pub fn authenticated( src: NodeId, seq: u32, heard: Vec, - mac_key: &Option<[u8; 32]>, - ) -> Self { + session_key: &[u8; 32], + ) -> Result { let ts = Self::now_ms(); - let mac = Self::compute_mac(src, seq, ts, &heard, mac_key); - Self { + let mac = Self::compute_mac(src, seq, ts, &heard, session_key)?; + Ok(Self { src, seq, ts, heard, mac, - } + }) + } + + /// Derive the per-session HELLO MAC key from a 32-byte session key. + fn derive_mac_key(session_key: &[u8; 32]) -> Result<[u8; 32], MeshError> { + let hk = Hkdf::::new(None, session_key); + let mut out = [0u8; 32]; + hk.expand(HELLO_MAC_INFO, &mut out) + .map_err(|_| MeshError::CryptoInternal)?; + Ok(out) } - /// E2.2 — Compute MAC over (src, seq, ts, heard[]) using ChaCha20-Poly1305 - /// The MAC key is typically derived from the session key with a context label + /// E2.2 - Compute MAC over (src, seq, ts, heard[]) using HMAC-SHA256. + /// Returns the first 16 bytes of the HMAC output. fn compute_mac( src: NodeId, seq: u32, ts: u64, heard: &[NodeId], - mac_key: &Option<[u8; 32]>, - ) -> [u8; 16] { - let key_bytes = mac_key.unwrap_or(HELLO_MAC_KEY); - let cipher = ChaCha20Poly1305::new(Key::from_slice(&key_bytes)); + session_key: &[u8; 32], + ) -> Result<[u8; 16], MeshError> { + let mac_key = Self::derive_mac_key(session_key)?; // Build MAC input: src || seq || ts || heard[] let n = heard.len().min(u8::MAX as usize); - let mut aad = Vec::with_capacity(12 + n * 4); + let mut aad = Vec::with_capacity(16 + n * 4); aad.extend_from_slice(&src.to_be_bytes()); aad.extend_from_slice(&seq.to_be_bytes()); aad.extend_from_slice(&ts.to_be_bytes()); @@ -92,31 +101,26 @@ impl Hello { aad.extend_from_slice(&id.to_be_bytes()); } - // Use empty plaintext, MAC is in the tag - let nonce = Nonce::from_slice(&[0u8; 12]); // fixed nonce for MAC-only mode - let ct = cipher - .encrypt( - nonce, - Payload { - msg: &[], - aad: &aad, - }, - ) - .expect("ChaCha20-Poly1305 MAC computation is infallible"); - - // Extract 16-byte tag (MAC) - let mut mac = [0u8; 16]; - mac.copy_from_slice(&ct[..16]); - mac + let mut mac = Hmac::::new_from_slice(&mac_key) + .map_err(|_| MeshError::CryptoInternal)?; + mac.update(&aad); + let result = mac.finalize().into_bytes(); + + let mut out = [0u8; 16]; + out.copy_from_slice(&result[..16]); + Ok(out) } - /// E2.2 — Verify MAC over (src, seq, ts, heard[]) - pub fn verify_mac(&self, mac_key: &Option<[u8; 32]>) -> bool { - let expected = Self::compute_mac(self.src, self.seq, self.ts, &self.heard, mac_key); - self.mac == expected + /// E2.2 - Verify MAC over (src, seq, ts, heard[]) + pub fn verify_mac(&self, session_key: &[u8; 32]) -> bool { + let Ok(expected) = Self::compute_mac(self.src, self.seq, self.ts, &self.heard, session_key) + else { + return false; + }; + self.mac.ct_eq(&expected).into() } - /// E2.3 — Check freshness: reject beacons older than HELLO_FRESHNESS_MS + /// E2.3 - Check freshness: reject beacons older than HELLO_FRESHNESS_MS pub fn is_fresh(&self) -> bool { let now = Self::now_ms(); // Handle timestamp wrap-around (unlikely for 64-bit but safe) @@ -142,17 +146,17 @@ impl Hello { let mut b = Vec::with_capacity(17 + n * 4); // +8 for ts, +16 for mac b.extend_from_slice(&self.src.to_be_bytes()); b.extend_from_slice(&self.seq.to_be_bytes()); - b.extend_from_slice(&self.ts.to_be_bytes()); // E2.3 — timestamp + b.extend_from_slice(&self.ts.to_be_bytes()); // E2.3 - timestamp b.push(n as u8); for id in self.heard.iter().take(n) { b.extend_from_slice(&id.to_be_bytes()); } - b.extend_from_slice(&self.mac); // E2.2 — MAC + b.extend_from_slice(&self.mac); // E2.2 - MAC b } pub fn parse(b: &[u8]) -> Option { - // New format: [src:4][seq:4][ts:8][n:1][heard:n×4][mac:16] + // New format: [src:4][seq:4][ts:8][n:1][heard:nx4][mac:16] if b.len() < 17 { // 4+4+8+1 minimum (no heard) +16 mac return None; @@ -187,7 +191,7 @@ impl Hello { }) } - /// Did this beacon report hearing `me`? (⇒ our forward link to `src` is up.) + /// Did this beacon report hearing `me`? (=> our forward link to `src` is up.) pub fn reports_hearing(&self, me: NodeId) -> bool { self.heard.contains(&me) } @@ -197,6 +201,10 @@ impl Hello { mod tests { use super::*; + fn session_key() -> [u8; 32] { + [42u8; 32] + } + #[test] fn hello_roundtrips() { let mac = [1u8; 16]; @@ -225,8 +233,8 @@ mod tests { #[test] fn mac_verifies_authentic_beacon() { - let key = Some([42u8; 32]); - let h = Hello::authenticated(7, 123, vec![1, 2, 3], &key); + let key = session_key(); + let h = Hello::authenticated(7, 123, vec![1, 2, 3], &key).unwrap(); // MAC should verify assert!(h.verify_mac(&key)); @@ -244,10 +252,10 @@ mod tests { #[test] fn mac_different_key_fails() { - let key1 = Some([1u8; 32]); - let key2 = Some([2u8; 32]); + let key1 = [1u8; 32]; + let key2 = [2u8; 32]; - let h = Hello::authenticated(7, 123, vec![1, 2], &key1); + let h = Hello::authenticated(7, 123, vec![1, 2], &key1).unwrap(); // Verification with wrong key fails assert!(!h.verify_mac(&key2)); @@ -255,11 +263,11 @@ mod tests { #[test] fn mac_prevents_false_metric_attack() { - // E2.4 — Attack simulation: Mallory tries to inflate ETX by forging heard[] - let key = Some([99u8; 32]); + // E2.4 - Attack simulation: Mallory tries to inflate ETX by forging heard[] + let key = [99u8; 32]; // Legitimate beacon from node 7 - let legitimate = Hello::authenticated(7, 1, vec![1, 2], &key); + let legitimate = Hello::authenticated(7, 1, vec![1, 2], &key).unwrap(); // Mallory creates fake beacon claiming node 7 heard everyone let fake = Hello { @@ -274,7 +282,7 @@ mod tests { assert!(!fake.verify_mac(&key)); // Even if Mallory recomputes MAC with wrong key, it fails - let fake_with_mac = Hello::authenticated(7, 1, vec![1, 2, 3, 4, 5], &key); + let fake_with_mac = Hello::authenticated(7, 1, vec![1, 2, 3, 4, 5], &key).unwrap(); assert_ne!(fake_with_mac.mac, legitimate.mac); } @@ -282,8 +290,8 @@ mod tests { #[test] fn fresh_beacon_accepted() { - let key = Some([5u8; 32]); - let h = Hello::authenticated(7, 123, vec![1], &key); + let key = [5u8; 32]; + let h = Hello::authenticated(7, 123, vec![1], &key).unwrap(); // Fresh beacon should pass assert!(h.is_fresh()); @@ -291,7 +299,7 @@ mod tests { #[test] fn old_beacon_rejected() { - let _key = Some([6u8; 32]); + let _key = [6u8; 32]; // Create beacon with old timestamp let now = Hello::now_ms(); @@ -305,10 +313,10 @@ mod tests { #[test] fn authenticated_hello_roundtrip() { - let key = Some([7u8; 32]); + let key = [7u8; 32]; // Create authenticated beacon - let h = Hello::authenticated(7, 456, vec![8, 9, 10], &key); + let h = Hello::authenticated(7, 456, vec![8, 9, 10], &key).unwrap(); // Serialize and parse let bytes = h.to_bytes(); diff --git a/src/lib.rs b/src/lib.rs index 600b6c12..292aed49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,49 +1,23 @@ -//! trios-mesh library — re-export hub for generated code. +//! trios-mesh library - encrypted, self-routing IP-over-radio mesh primitives. //! -//! All business logic lives in gen/rust/ (generated from specs/*.t27). -//! This file ONLY re-exports. No hand-written logic. -//! -//! Pipeline: specs/*.t27 -> t27c gen-rust -> gen/rust/ -> src/ +//! The hand-written modules below are the current runtime surface. The +//! generated `gen/rust/` stubs are excluded from compilation until `t27c` is +//! available to produce valid Rust from `specs/*.t27`. //! //! phi^2 + phi^-2 = 3 -// Re-export all generated modules +// Tests assert on infallible test-only roundtrips; unwrap/expect are allowed +// in test code while production code remains covered by the workspace deny lint. +#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))] + pub mod crypto; -pub mod wire; -pub mod router; -pub mod routing; -pub mod modem; -pub mod gf16; pub mod daemon; pub mod discovery; - -// Re-export generated mesh components -#[path = "../gen/rust/mesh_routing.rs"] -pub mod mesh_routing; - -#[path = "../gen/rust/etx.rs"] -pub mod etx; - -#[path = "../gen/rust/adaptive_routing.rs"] -pub mod adaptive_routing; - -#[path = "../gen/rust/multipath_routing.rs"] -pub mod multipath_routing; - -#[path = "../gen/rust/frame_buffer.rs"] -pub mod frame_buffer; - -#[path = "../gen/rust/flow_control.rs"] -pub mod flow_control; - -#[path = "../gen/rust/health_dashboard.rs"] -pub mod health_dashboard; - -#[path = "../gen/rust/anomaly_detector.rs"] -pub mod anomaly_detector; - -#[path = "../gen/rust/quarantine_manager.rs"] -pub mod quarantine_manager; +pub mod gf16; +pub mod modem; +pub mod router; +pub mod routing; +pub mod wire; // Types used across the crate pub type NodeId = u32; diff --git a/src/router.rs b/src/router.rs index c04b014c..17c060b8 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,4 +1,4 @@ -//! M2 — IP-over-radio data plane (tri-net#11). +//! M2 - IP-over-radio data plane (tri-net#11). //! //! A [`MeshRouter`] reads IP packets from the local TUN netdev, picks a next hop //! toward the destination using the [`EtxTable`] metric, seals each packet @@ -23,7 +23,7 @@ use std::net::Ipv4Addr; /// Default hop budget for a freshly originated packet. pub const DEFAULT_TTL: u8 = 8; -/// Mesh subnet `10.42.0.0/24`: NodeId `n` (1..=254) ⇔ `10.42.0.n`. +/// Mesh subnet `10.42.0.0/24`: NodeId `n` (1..=254) maps to `10.42.0.n`. pub fn mesh_ip(id: NodeId) -> Ipv4Addr { Ipv4Addr::new(10, 42, 0, (id & 0xff) as u8) } @@ -48,16 +48,16 @@ pub enum DropReason { /// Frame from a node we have no session with, or it failed to open. Unopened(MeshError), /// Outbound seal failed (e.g. the per-key rekey hard cap was reached before - /// a ratchet step) — the frame is dropped rather than risking nonce reuse. + /// a ratchet step) - the frame is dropped rather than risking nonce reuse. SealFailed(MeshError), - /// E3.2 — Frame header.src != actual link peer (spoof attempt). + /// E3.2 - Frame header.src != actual link peer (spoof attempt). SrcSpoof, } /// Outcome of handling one frame. #[derive(Debug, PartialEq, Eq)] pub enum Delivery { - /// Packet was for this node — hand the payload up to the local TUN. + /// Packet was for this node - hand the payload up to the local TUN. Local(Vec), /// Packet was re-sealed and forwarded to `next_hop`. Forwarded(NodeId), @@ -100,11 +100,11 @@ pub struct MeshRouter { etx: EtxTable, /// One crypto session + transport per directly-linked neighbor. links: HashMap, - /// Learned overrides: destination → next-hop neighbor. + /// Learned overrides: destination -> next-hop neighbor. routes: HashMap, /// E5: Ranked next-hops (k=2) for fast failover. ranked_hops: HashMap, - /// E5: Candidate routes for ranked hops (dst → vec of (next_hop, path_etx)). + /// E5: Candidate routes for ranked hops (dst -> vec of (next_hop, path_etx)). ranked_candidates: HashMap>, } @@ -157,7 +157,7 @@ impl MeshRouter { self.etx.record(peer, we_heard, they_heard); } - /// Neighbor ETX snapshot (id, etx), sorted by id — for status/printing. + /// Neighbor ETX snapshot (id, etx), sorted by id - for status/printing. pub fn neighbors(&self) -> Vec<(NodeId, f32)> { self.etx.neighbors() } @@ -181,12 +181,12 @@ impl MeshRouter { for (dst, ranked) in self.ranked_hops.iter_mut() { if ranked.primary == Some(peer) { - // Hot-swap: primary dead → promote backup to primary + // Hot-swap: primary dead -> promote backup to primary ranked.primary = ranked.backup; ranked.backup = None; // Need to recompute backup later routes_to_update.push(*dst); } else if ranked.backup == Some(peer) { - // Backup dead → just clear it (recompute later) + // Backup dead -> just clear it (recompute later) ranked.backup = None; } } @@ -205,7 +205,7 @@ impl MeshRouter { /// Learn a path route to `dst` via `next_hop` with advertised ETX `adv_etx`. /// Computes cumulative path ETX (link ETX + advertised ETX) and applies - /// RFC 8966 §3.7 feasibility check before accepting the route. + /// RFC 8966 section 3.7 feasibility check before accepting the route. /// Returns true if the route was learned (passed feasibility). pub fn learn_route(&mut self, dst: NodeId, next_hop: NodeId, adv_etx: f32) -> bool { // Compute cumulative path ETX @@ -279,7 +279,13 @@ impl MeshRouter { by_nh.retain(|nh, _| self.etx.etx(*nh).is_none_or(|e| e.is_finite())); let mut candidates: Vec<(NodeId, f32)> = by_nh.into_iter().collect(); - candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + // NaN metrics should never appear (ETX is finite by construction), but + // partial_cmp can return None for NaN. Use a total order that treats NaN + // as worse than any finite value to avoid a panic in the routing hot path. + candidates.sort_by(|a, b| { + a.1.partial_cmp(&b.1) + .unwrap_or_else(|| a.1.is_nan().cmp(&b.1.is_nan()).reverse()) + }); let ranked = if candidates.is_empty() { RankedNextHops::new() @@ -314,7 +320,7 @@ impl MeshRouter { if dst == self.id { return None; } - // Use the direct link unless its ETX has gone infinite (dead) — that is + // Use the direct link unless its ETX has gone infinite (dead) - that is // what lets traffic self-heal around a failed direct neighbor via a relay. let direct_dead = self.etx.etx(dst).is_some_and(|e| e.is_infinite()); if self.links.contains_key(&dst) && !direct_dead { @@ -397,7 +403,7 @@ impl MeshRouter { None => return Delivery::Dropped(DropReason::Unopened(MeshError::Auth)), }; - // E3.1 — Src cross-check for HELLO frames only + // E3.1 - Src cross-check for HELLO frames only // HELLO beacons MUST have src == from (node announces itself) // Data frames can have src != from (multi-hop relay is normal) // W3 mitigation: prevent neighbor from spoofing HELLO source @@ -469,12 +475,8 @@ mod tests { } } impl VecTransport { - fn take(&self) -> Vec { - self.q - .lock() - .unwrap() - .pop_front() - .expect("a frame was sent") + fn take(&self) -> Option> { + self.q.lock().unwrap_or_else(|p| p.into_inner()).pop_front() } } @@ -483,7 +485,10 @@ mod tests { let a = Handshake::new(); let b = Handshake::new(); let (ap, bp) = (a.public, b.public); - (a.complete(&bp, true), b.complete(&ap, false)) + ( + a.complete(&bp, true).unwrap(), + b.complete(&ap, false).unwrap(), + ) } #[test] @@ -504,7 +509,7 @@ mod tests { b.add_link(1, sb, Box::new(VecTransport::default())); assert_eq!(a.send_ip(2, b"ip-packet"), Delivery::Forwarded(2)); - let frame = t.take(); + let frame = t.take().expect("a frame was sent"); assert_eq!( b.handle_frame(1, &frame), Delivery::Local(b"ip-packet".to_vec()) @@ -513,7 +518,7 @@ mod tests { #[test] fn two_hop_relay_with_hop_by_hop_crypto() { - // A(1) — C(3) — B(2). A has no direct link to B; it must relay via C. + // A(1) - C(3) - B(2). A has no direct link to B; it must relay via C. let (a_c, c_a) = sessions(); // A<->C let (c_b, b_c) = sessions(); // C<->B let ac = VecTransport::default(); // A -> C @@ -528,19 +533,19 @@ mod tests { c.add_link(2, c_b, Box::new(cb.clone())); b.add_link(3, b_c, Box::new(VecTransport::default())); - // A learns C is a good neighbor so best_next_hop(→ relay) resolves to C. + // A learns C is a good neighbor so best_next_hop(-> relay) resolves to C. for _ in 0..4 { a.observe(3, true, true); } assert_eq!(a.next_hop(2), Some(3), "A relays toward B via C"); - // A originates to B → goes to C. + // A originates to B -> goes to C. assert_eq!(a.send_ip(2, b"hello over 2 hops"), Delivery::Forwarded(3)); - let f1 = ac.take(); + let f1 = ac.take().expect("a frame was sent"); - // C receives from A, sees dst=B, re-seals under the C<->B session → B. + // C receives from A, sees dst=B, re-seals under the C<->B session -> B. assert_eq!(c.handle_frame(1, &f1), Delivery::Forwarded(2)); - let f2 = cb.take(); + let f2 = cb.take().expect("a frame was sent"); assert_ne!( f1, f2, "each hop is independently encrypted (different ciphertext)" @@ -587,7 +592,7 @@ mod tests { let (sa, _sb) = sessions(); let mut a = MeshRouter::new(1, 16); a.add_link(2, sa, Box::new(VecTransport::default())); - // A never linked node 9 → cannot open its frame. + // A never linked node 9 -> cannot open its frame. let junk = vec![0u8; Header::LEN + 32]; assert_eq!( a.handle_frame(9, &junk), @@ -603,7 +608,7 @@ mod tests { let mut a = MeshRouter::new(1, 4); a.add_link(2, s2, Box::new(VecTransport::default())); a.add_link(3, s3, Box::new(VecTransport::default())); - // Both links healthy → route to 3 is direct. + // Both links healthy -> route to 3 is direct. for _ in 0..4 { a.observe(2, true, true); a.observe(3, true, true); @@ -646,7 +651,7 @@ mod tests { spoofed_hello[9] = 1; spoofed_hello[10] = 8; // ttl - // A receives from 3 but HELLO says src=2 → should be dropped + // A receives from 3 but HELLO says src=2 -> should be dropped let result = a.handle_frame(3, &spoofed_hello); // Will fail MAC check first (not a valid encrypted frame), @@ -697,8 +702,8 @@ mod tests { #[test] fn multi_hop_data_relay_not_affected_by_src_check() { - // E3 — Demonstrate that data frames with src != from are accepted (multi-hop) - // A(1) — C(3). C relays frame from A (src=1) to someone else. + // E3 - Demonstrate that data frames with src != from are accepted (multi-hop) + // A(1) - C(3). C relays frame from A (src=1) to someone else. let (s_a_c, s_c_a) = sessions(); let t = VecTransport::default(); @@ -710,20 +715,20 @@ mod tests { // A sends frame assert_eq!(a.send_ip(3, b"hello from A"), Delivery::Forwarded(3)); - let frame = t.take(); + let frame = t.take().expect("a frame was sent"); // This is a DATA frame (kind=1) from A to C // We'll modify it to simulate relay: src=1, but received from a peer // Verify it's not HELLO (which would require src == from) assert_ne!(frame[1], FrameKind::Hello as u8); - // C receives from A with src=1 → NOT SrcSpoof (data frames can have src == from) + // C receives from A with src=1 -> NOT SrcSpoof (data frames can have src == from) // This is normal single-hop traffic assert!(matches!(c.handle_frame(1, &frame), Delivery::Local(_))); // Additional test: craft a data frame with src != from (simulating relay) // In real multi-hop, C would receive from A (src=1, from=1) and relay to B - // B would receive from C (src=1, from=3) → this should NOT be SrcSpoof + // B would receive from C (src=1, from=3) -> this should NOT be SrcSpoof let _relayed_frame = frame.clone(); // Change dst to simulate B (not actually routing, just testing src check) // relayed_frame[6..10] = 2.to_be_bytes(); // Would need to re-encrypt diff --git a/src/routing.rs b/src/routing.rs index 1ae8cf75..e0d90fba 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -149,7 +149,10 @@ impl EtxTable { .iter() .map(|(id, l)| (*id, l.etx())) .filter(|(_, etx)| etx.is_finite()) - .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap()) + .min_by(|a, b| { + a.1.partial_cmp(&b.1) + .unwrap_or_else(|| a.1.is_nan().cmp(&b.1.is_nan()).reverse()) + }) } /// RFC 8966 §3.7 feasibility check: a route is feasible if its metric is diff --git a/src/wire.rs b/src/wire.rs index d45375e1..49bedabe 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -3,32 +3,56 @@ //! //! Anchor: phi^2 + phi^-2 = 3. //! -//! # T27-first partial flip +//! # T27-first note //! -//! Constants (`VERSION`, `KIND_HELLO`, `KIND_DATA`, `HEADER_LEN`) and pure -//! predicates (`frame_kind_valid`, `header_byte`, `parse_accepts`) live in -//! `specs/wire.t27` and are auto-generated into `gen/rust/wire.rs` via the -//! t27c bootstrap compiler. This module re-exports them and wraps them in -//! ergonomic Rust types. See `docs/T27_FIRST_MIGRATION.md`. +//! The original `specs/wire.t27` is the SSOT for constants and predicates. The +//! generated `gen/rust/wire.rs` currently contains stub arithmetic (`return ()`), +//! so this module implements the wire spec directly until `t27c` produces valid +//! Rust. Constants and logic below mirror `specs/wire.t27` byte-for-byte. use crate::routing::NodeId; -// Auto-generated from specs/wire.t27 by t27c gen-rust. -// The t27c-0.1.0 emitter produces literal `return` statements and extra -// parentheses around every expression. This is idiomatic for the T27 language -// but not for Rust, so we scope clippy/rustc lints down here rather than -// hand-editing the generated file (gen/ is untouchable; see -// docs/T27_FIRST_MIGRATION.md). Cleaner Rust rendering is upstream work on -// gHashTag/t27 (needless_return / unnecessary_parens in expr_to_rust). -#[allow(clippy::needless_return, unused_parens)] -pub mod gen { - include!("../gen/rust/wire.rs"); +pub const VERSION: u8 = 1; +pub const KIND_HELLO: u8 = 0; +pub const KIND_DATA: u8 = 1; +pub const HEADER_LEN: usize = 11; // [ver:1][kind:1][src:4][dst:4][ttl:1] + +/// Returns true iff `k` is a known frame kind. +pub fn frame_kind_valid(k: u8) -> bool { + k <= KIND_DATA +} + +/// The i-th big-endian byte of a 32-bit word (i=0 is most significant). +pub fn be_byte(w: u32, i: usize) -> u8 { + match i { + 0 => ((w >> 24) & 0xff) as u8, + 1 => ((w >> 16) & 0xff) as u8, + 2 => ((w >> 8) & 0xff) as u8, + 3 => (w & 0xff) as u8, + _ => 0, + } +} + +/// Reassemble a big-endian u32 from four bytes (b0 is most significant). +pub fn u32_be(b0: u8, b1: u8, b2: u8, b3: u8) -> u32 { + ((b0 as u32) << 24) | ((b1 as u32) << 16) | ((b2 as u32) << 8) | (b3 as u32) } -pub use gen::{ - be_byte, frame_kind_valid, header_byte, parse_accepts, u32_be, HEADER_LEN, KIND_DATA, - KIND_HELLO, VERSION, -}; +/// The idx-th byte of the serialized 11-byte header. +pub fn header_byte(kind: u8, src: u32, dst: u32, ttl: u8, idx: usize) -> u8 { + match idx { + 0 => VERSION, + 1 => kind, + 2..=5 => be_byte(src, idx - 2), + 6..=9 => be_byte(dst, idx - 6), + _ => ttl, + } +} + +/// A two-byte prefix is acceptable iff version matches and kind is valid. +pub fn parse_accepts(b0: u8, b1: u8) -> bool { + b0 == VERSION && frame_kind_valid(b1) +} #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FrameKind { @@ -88,10 +112,6 @@ impl Header { } Some(Self { kind: FrameKind::from_u8(b[1])?, - // SSOT: reassemble big-endian u32 via the auto-generated u32_be from - // specs/wire.t27 (byte-order equivalent to u32::from_be_bytes; see - // docs/T27_FIRST_MIGRATION.md). Keeps the parse-path arithmetic under - // the spec-drift-guard CI umbrella. src: u32_be(b[2], b[3], b[4], b[5]), dst: u32_be(b[6], b[7], b[8], b[9]), ttl: b[10], @@ -117,7 +137,7 @@ mod tests { } #[test] - fn t27_gen_constants_match_hand_written() { + fn wire_constants_match_spec() { assert_eq!(VERSION, 1); assert_eq!(KIND_HELLO, 0); assert_eq!(KIND_DATA, 1); @@ -125,7 +145,7 @@ mod tests { } #[test] - fn t27_gen_predicates_match_semantics() { + fn wire_predicates_match_spec() { assert!(frame_kind_valid(KIND_HELLO)); assert!(frame_kind_valid(KIND_DATA)); assert!(!frame_kind_valid(2));