-
-
Save RandyMcMillan/7c6a394a6d206a931ac5754e0413c245 to your computer and use it in GitHub Desktop.
byz_fee_engine.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use chrono::{DateTime, Duration, Utc, Timelike}; | |
| use std::sync::{Arc, Mutex}; | |
| use std::time::Duration as StdDuration; | |
| // --- CONSTANTS --- | |
| const SHA256_K: [u32; 64] = [ | |
| 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
| 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
| 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
| 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
| 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
| 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
| 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
| 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, | |
| ]; | |
| // --- GIT-COMPLIANT SHA-1 ENGINE --- | |
| fn git_sha1(data: &[u8]) -> String { | |
| let mut h0: u32 = 0x67452301; let mut h1: u32 = 0xEFCDAB89; | |
| let mut h2: u32 = 0x98BADCFE; let mut h3: u32 = 0x10325476; | |
| let mut h4: u32 = 0xC3D2E1F0; | |
| let mut padded = data.to_vec(); | |
| let bit_len = (padded.len() as u64) * 8; | |
| padded.push(0x80); | |
| while (padded.len() * 8) % 512 != 448 { padded.push(0); } | |
| padded.extend_from_slice(&bit_len.to_be_bytes()); | |
| for chunk in padded.chunks(64) { | |
| let mut w = [0u32; 80]; | |
| for i in 0..16 { w[i] = u32::from_be_bytes([chunk[i*4], chunk[i*4+1], chunk[i*4+2], chunk[i*4+3]]); } | |
| for i in 16..80 { w[i] = (w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]).rotate_left(1); } | |
| let (mut a, mut b, mut c, mut d, mut e) = (h0, h1, h2, h3, h4); | |
| for i in 0..80 { | |
| let (f, k) = match i { | |
| 0..=19 => ((b & c) | ((!b) & d), 0x5A827999), | |
| 20..=39 => (b ^ c ^ d, 0x6ED9EBA1), | |
| 40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1BBCDC), | |
| _ => (b ^ c ^ d, 0xCA62C1D6), | |
| }; | |
| let temp = a.rotate_left(5).wrapping_add(f).wrapping_add(e).wrapping_add(k).wrapping_add(w[i]); | |
| e = d; d = c; c = b.rotate_left(30); b = a; a = temp; | |
| } | |
| h0 = h0.wrapping_add(a); h1 = h1.wrapping_add(b); | |
| h2 = h2.wrapping_add(c); h3 = h3.wrapping_add(d); h4 = h4.wrapping_add(e); | |
| } | |
| format!("{:08x}{:08x}{:08x}{:08x}{:08x}", h0, h1, h2, h3, h4) | |
| } | |
| // --- SHA-256 ENGINE --- | |
| fn sha256(data: &[u8]) -> String { | |
| let mut h: [u32; 8] = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]; | |
| let mut padded = data.to_vec(); | |
| let bit_len = (padded.len() as u64) * 8; | |
| padded.push(0x80); | |
| while (padded.len() * 8) % 512 != 448 { padded.push(0); } | |
| padded.extend_from_slice(&bit_len.to_be_bytes()); | |
| for chunk in padded.chunks(64) { | |
| let mut w = [0u32; 64]; | |
| for i in 0..16 { w[i] = u32::from_be_bytes([chunk[i*4], chunk[i*4+1], chunk[i*4+2], chunk[i*4+3]]); } | |
| for i in 16..64 { | |
| let s0 = w[i-15].rotate_right(7) ^ w[i-15].rotate_right(18) ^ (w[i-15] >> 3); | |
| let s1 = w[i-2].rotate_right(17) ^ w[i-2].rotate_right(19) ^ (w[i-2] >> 10); | |
| w[i] = w[i-16].wrapping_add(s0).wrapping_add(w[i-7]).wrapping_add(s1); | |
| } | |
| let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h_val] = h; | |
| for i in 0..64 { | |
| let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25); | |
| let ch = (e & f) ^ ((!e) & g); | |
| let temp1 = h_val.wrapping_add(s1).wrapping_add(ch).wrapping_add(SHA256_K[i]).wrapping_add(w[i]); | |
| let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22); | |
| let maj = (a & b) ^ (a & c) ^ (b & c); | |
| let temp2 = s0.wrapping_add(maj); | |
| h_val = g; g = f; f = e; e = d.wrapping_add(temp1); d = c; c = b; b = a; a = temp1.wrapping_add(temp2); | |
| } | |
| h = [h[0].wrapping_add(a), h[1].wrapping_add(b), h[2].wrapping_add(c), h[3].wrapping_add(d), | |
| h[4].wrapping_add(e), h[5].wrapping_add(f), h[6].wrapping_add(g), h[7].wrapping_add(h_val)]; | |
| } | |
| h.iter().map(|x| format!("{:08x}", x)).collect() | |
| } | |
| // --- POOL STRUCTURES --- | |
| pub struct BlockTemplate { | |
| pub version: u32, | |
| pub prev_block: String, | |
| pub merkle_root: String, | |
| pub pool_target: String, | |
| } | |
| pub struct DecentralizedPoolNode { | |
| pub miner_id: String, | |
| pub current_template: Arc<Mutex<BlockTemplate>>, | |
| pub shares_submitted: u64, | |
| } | |
| impl DecentralizedPoolNode { | |
| pub fn new(miner_id: &str, initial_template: BlockTemplate) -> Self { | |
| Self { | |
| miner_id: miner_id.to_string(), | |
| current_template: Arc::new(Mutex::new(initial_template)), | |
| shares_submitted: 0, | |
| } | |
| } | |
| pub fn verify_incoming_share(&mut self, nonce: u64) -> bool { | |
| let template = self.current_template.lock().unwrap(); | |
| let payload = format!("{}{}{}{}", template.version, template.prev_block, template.merkle_root, nonce); | |
| let intermediate_hash = sha256(payload.as_bytes()); | |
| let final_hash = sha256(intermediate_hash.as_bytes()); | |
| if final_hash.starts_with(&template.pool_target) { | |
| self.shares_submitted += 1; | |
| true | |
| } else { | |
| false | |
| } | |
| } | |
| } | |
| // --- BYZANTINE FEE ENGINE TYPES --- | |
| #[derive(Debug, Clone, Copy)] | |
| pub struct ByzantineFeeProposal { | |
| pub node_id: usize, | |
| pub reported_fee_rate: u64, | |
| pub is_byzantine: bool, | |
| } | |
| pub fn calculate_bft_median_fee(mut proposals: Vec<ByzantineFeeProposal>) -> u64 { | |
| let n = proposals.len(); | |
| if n == 0 { return 1; } | |
| let f = n / 3; | |
| proposals.sort_by_key(|p| p.reported_fee_rate); | |
| let valid_range = &proposals[f..(n - f)]; | |
| let median_index = valid_range.len() / 2; | |
| valid_range[median_index].reported_fee_rate | |
| } | |
| // --- TIME & FEE NETWORK STATE MACHINE --- | |
| #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] | |
| pub enum SyncStage { Hour, Minute, Second, FeeConsensus, NonceGrind1Bit, NonceGrind2Bit } | |
| pub struct SyncNode { | |
| pub id: usize, | |
| pub adjustment: Duration, | |
| pub stage: SyncStage, | |
| pub start_nonce: u64, | |
| pub nonce: u64, | |
| pub success: bool, | |
| pub last_hash: String, | |
| pub internal_fee_estimation: u64, | |
| pub is_byzantine_adversary: bool, | |
| } | |
| impl SyncNode { | |
| pub fn new(id: usize, offset_sec: i64, total_nodes: usize, is_adversary: bool) -> Self { | |
| let stride = u64::MAX / (total_nodes as u64); | |
| let start_nonce = (id as u64) * stride; | |
| let fee_basis = if is_adversary { 9999 } else { 15 + (id as u64 % 5) * 5 }; | |
| Self { | |
| id, | |
| adjustment: Duration::seconds(offset_sec), | |
| stage: SyncStage::Hour, | |
| start_nonce, | |
| nonce: start_nonce, | |
| success: false, | |
| last_hash: String::from("0000000000000000000000000000000000000000"), | |
| internal_fee_estimation: fee_basis, | |
| is_byzantine_adversary: is_adversary, | |
| } | |
| } | |
| pub fn get_logical_utc(&self) -> DateTime<Utc> { | |
| Utc::now() + self.adjustment | |
| } | |
| pub fn broadcast_fee_proposal(&self) -> ByzantineFeeProposal { | |
| ByzantineFeeProposal { | |
| node_id: self.id, | |
| reported_fee_rate: self.internal_fee_estimation, | |
| is_byzantine: self.is_byzantine_adversary, | |
| } | |
| } | |
| pub fn update_stage(&mut self, spread: i64, all_same_minute: bool, global_fee_agreed: bool, global_1bit_reached: bool) { | |
| match self.stage { | |
| SyncStage::Hour => if spread < 3600 { self.stage = SyncStage::Minute; }, | |
| SyncStage::Minute => if spread < 60 { self.stage = SyncStage::Second; }, | |
| SyncStage::Second => if spread == 0 && all_same_minute { self.stage = SyncStage::FeeConsensus; }, | |
| SyncStage::FeeConsensus => if global_fee_agreed { self.stage = SyncStage::NonceGrind1Bit; }, | |
| SyncStage::NonceGrind1Bit => { | |
| if spread > 0 || !all_same_minute { | |
| self.stage = SyncStage::Second; self.success = false; self.nonce = self.start_nonce; | |
| } else if global_1bit_reached { | |
| self.stage = SyncStage::NonceGrind2Bit; self.success = false; | |
| } | |
| }, | |
| SyncStage::NonceGrind2Bit => { | |
| if spread > 0 || !all_same_minute { | |
| self.stage = SyncStage::Second; self.success = false; self.nonce = self.start_nonce; | |
| } | |
| } | |
| } | |
| } | |
| pub fn grind_nonce(&mut self, target: &str, template: &BlockTemplate, agreed_network_fee: u64) { | |
| let time = self.get_logical_utc(); | |
| let minute = time.minute(); | |
| loop { | |
| let input = format!( | |
| "BLOCK-{}-{}-{}-{}-FEE:{}", | |
| template.prev_block, | |
| template.merkle_root, | |
| minute, | |
| self.nonce, | |
| agreed_network_fee | |
| ); | |
| let hash = if target == "00" { | |
| let round1 = sha256(input.as_bytes()); | |
| sha256(round1.as_bytes()) | |
| } else { | |
| git_sha1(input.as_bytes()) | |
| }; | |
| if target == "00" { | |
| if hash.starts_with("00") { | |
| self.last_hash = hash; self.success = true; break; | |
| } else if hash.starts_with("0") { | |
| self.last_hash = hash; self.nonce += 1; break; | |
| } | |
| } else { | |
| self.last_hash = hash; | |
| if self.last_hash.starts_with(target) { self.success = true; } else { self.nonce += 1; } | |
| break; | |
| } | |
| self.nonce += 1; | |
| } | |
| } | |
| } | |
| fn get_median_diff(timestamps: &[i64], current: i64) -> i64 { | |
| let mut diffs: Vec<i64> = timestamps.iter().map(|t| t - current).collect(); | |
| diffs.sort(); | |
| diffs[diffs.len() / 2] | |
| } | |
| // --- MAIN RUNTIME LOOP --- | |
| fn main() { | |
| let total_nodes = 10; | |
| let initial_job = BlockTemplate { | |
| version: 4, | |
| prev_block: "000000000000000000021c33f24bf7aef12d".to_string(), | |
| merkle_root: "94b8e19c20cb3ffbb123a".to_string(), | |
| pool_target: "00".to_string(), | |
| }; | |
| let mut pool_node = DecentralizedPoolNode::new("pleb_pool_partitioner", initial_job); | |
| println!("Decentralized Sharded Pool Node Active. ID: {}", pool_node.miner_id); | |
| let mut nodes: Vec<SyncNode> = (0..total_nodes).map(|i| { | |
| let offset = match i { 0..=2 => 10, 3..=5 => 5, _ => 2 }; | |
| let is_byzantine = i < 2; | |
| SyncNode::new(i, offset, total_nodes, is_byzantine) | |
| }).collect(); | |
| let mut round = 1; | |
| let mut final_consensus_fee: u64 = 0; | |
| loop { | |
| let current_times: Vec<DateTime<Utc>> = nodes.iter().map(|n| n.get_logical_utc()).collect(); | |
| let timestamps: Vec<i64> = current_times.iter().map(|t| t.timestamp()).collect(); | |
| let spread = (timestamps.iter().max().unwrap() - timestamps.iter().min().unwrap()).abs(); | |
| let all_same_minute = current_times.iter().all(|t| t.minute() == current_times[0].minute()); | |
| let proposals: Vec<ByzantineFeeProposal> = nodes.iter().map(|n| n.broadcast_fee_proposal()).collect(); | |
| let calculated_bft_fee = calculate_bft_median_fee(proposals); | |
| let global_fee_agreed = nodes.iter().all(|n| n.stage >= SyncStage::FeeConsensus); | |
| let global_1bit_reached = nodes.iter().all(|n| n.stage == SyncStage::NonceGrind1Bit && n.success); | |
| let has_consensus = { | |
| let active_template = pool_node.current_template.lock().unwrap(); | |
| println!( | |
| "\n--- [ROUND {:03}] Spread:{}s | TimeMinConsensus:{} | Secure BFT Fee: {} sat/vB ---", | |
| round, spread, all_same_minute, calculated_bft_fee | |
| ); | |
| final_consensus_fee = calculated_bft_fee; | |
| for i in 0..total_nodes { | |
| // Save original local expectation for logging outputs before phase transition overrides it | |
| let local_untrusted_fee = nodes[i].internal_fee_estimation; | |
| nodes[i].update_stage(spread, all_same_minute, global_fee_agreed, global_1bit_reached); | |
| match nodes[i].stage { | |
| SyncStage::Hour | SyncStage::Minute | SyncStage::Second => { | |
| let d = get_median_diff(×tamps, timestamps[i]); | |
| let step = if nodes[i].stage == SyncStage::Second { d.signum() } else { d / 2 }; | |
| nodes[i].adjustment = nodes[i].adjustment + Duration::seconds(step); | |
| }, | |
| SyncStage::FeeConsensus => { | |
| nodes[i].internal_fee_estimation = calculated_bft_fee; | |
| }, | |
| SyncStage::NonceGrind1Bit => { | |
| if !nodes[i].success { | |
| nodes[i].grind_nonce("0", &active_template, calculated_bft_fee); | |
| } | |
| } | |
| SyncStage::NonceGrind2Bit => { | |
| if !nodes[i].success { | |
| nodes[i].grind_nonce("00", &active_template, calculated_bft_fee); | |
| } | |
| } | |
| }; | |
| let mark = if nodes[i].success { "SOLVED " } else { "MINING" }; | |
| let role = if nodes[i].is_byzantine_adversary { "MALICIOUS" } else { "HONEST " }; | |
| // ADDED: Prints internal untrusted local fee rate side-by-side with the agreed BFT fee rate target | |
| println!( | |
| "N{:02} [{}] | Stage:{} | Time: {} | LocalFee: {:4} sat/vB | BftFee: {:4} sat/vB | Nonce: {:x} | {} | HASH: {}", | |
| i, role, nodes[i].stage as u8, current_times[i].format("%H:%M:%S"), | |
| local_untrusted_fee, nodes[i].internal_fee_estimation, nodes[i].nonce, mark, nodes[i].last_hash | |
| ); | |
| } | |
| nodes.iter().all(|n| n.stage == SyncStage::NonceGrind2Bit && n.success) | |
| }; | |
| if has_consensus { | |
| for i in 0..total_nodes { | |
| pool_node.verify_incoming_share(nodes[i].nonce); | |
| } | |
| println!("\n>>> SUCCESS: BYZANTINE FEE CONSENSUS COMPLETED AND BOUND TO NONCE PAYLOAD <<<"); | |
| println!("Final Network Agreed Fee Rate Base: {} sat/vB", final_consensus_fee); | |
| break; | |
| } | |
| round += 1; | |
| std::thread::sleep(StdDuration::from_millis(15)); | |
| if round > 1000 { break; } | |
| } | |
| } | |
| // --- TESTING SUITE --- | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[test] | |
| fn test_byzantine_fee_filtering_outliers() { | |
| let proposals = vec![ | |
| ByzantineFeeProposal { node_id: 0, reported_fee_rate: 9999, is_byzantine: true }, | |
| ByzantineFeeProposal { node_id: 1, reported_fee_rate: 1, is_byzantine: true }, | |
| ByzantineFeeProposal { node_id: 2, reported_fee_rate: 25, is_byzantine: false }, | |
| ByzantineFeeProposal { node_id: 3, reported_fee_rate: 30, is_byzantine: false }, | |
| ByzantineFeeProposal { node_id: 4, reported_fee_rate: 20, is_byzantine: false }, | |
| ]; | |
| let settled_fee = calculate_bft_median_fee(proposals); | |
| assert!(settled_fee >= 20 && settled_fee <= 30); | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=bb4c8eefb66b7f63d06d3a8c473d02ed