use crate::common::skein::{skein_256_hash_data, skein_512_hash_data}; use crate::common::types::Transaction; use crate::records::block_height::get_block_height::get_height; use crate::records::memory::averages::{calculate_averages, update_block_data}; use crate::sled::Db; use crate::to_string; use crate::wallets::structures::Wallet; use crate::Cursor; use crate::Duration; use crate::Serialize; use crate::{decode, encode}; use crate::{AsyncReadExt, AsyncWriteExt}; pub const TIMESTAMP_OFFSET: usize = 0; pub const MINER_OFFSET: usize = TIMESTAMP_OFFSET + 4; pub const PREVIOUS_HASH_OFFSET: usize = MINER_OFFSET + Wallet::SHORT_ADDRESS_BYTES_LENGTH; pub const DIFFICULTY_OFFSET: usize = PREVIOUS_HASH_OFFSET + 32; pub const NONCE_OFFSET: usize = DIFFICULTY_OFFSET + 8; pub const VRF_OFFSET: usize = NONCE_OFFSET + 1; pub const PROOF_OFFSET: usize = VRF_OFFSET + 16; pub const UNMINED_BLOCK_BYTES: usize = 4 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + 32 + 8 + 1; pub const VRF_BLOCK_BYTES: usize = UNMINED_BLOCK_BYTES + 16 + Wallet::SIGNATURE_LENGTH; // UnminedBlock is the deterministic header data that exists before // the miner wallet adds the VRF proof. #[derive(Debug, Serialize, Clone)] // 67 bytes pub struct UnminedBlock { pub timestamp: u32, // 4 bytes block timestamp pub miner: String, // 22 bytes miner short address pub previous_hash: String, // 32 bytes parent block hash pub next_block_difficulty: u64, // 8 bytes difficulty for this block pub nonce: u8, // 1 byte nonce searched by mining workers } // VrfBlock adds the miner's signed proof and derived VRF number to the header. #[derive(Debug, Serialize, Clone)] // 749 bytes pub struct VrfBlock { pub unmined_block: UnminedBlock, // 67 bytes unsigned block header fields pub vrf: u128, // 16 bytes random number derived from proof pub proof: String, // 666 bytes miner signature proof } // Block stores the VRF header plus the ordered transaction list. #[derive(Debug, Serialize)] // header is 749 bytes plus transactions pub struct Block { pub vrf_block: VrfBlock, pub transactions: Vec, } impl UnminedBlock { // Create the unmined block header fields. pub async fn new( timestamp: u32, miner: &str, previous_hash: &str, next_block_difficulty: u64, nonce: u8, ) -> Self { Self { timestamp, miner: miner.to_string(), previous_hash: previous_hash.to_string(), next_block_difficulty, nonce, } } pub async fn generate_random_number(input: &str) -> u128 { // Hash the proof with Skein512, then fold the 64-byte result // into one u128 value by XORing four 16-byte chunks. let hash = skein_512_hash_data(input); let hash_bytes = decode(&hash).expect("Failed to decode hash"); if hash_bytes.len() != 64 { panic!("Hash must be exactly 64 bytes long."); } let a = u128::from_le_bytes( hash_bytes[0..16] .try_into() .expect("Chunk A must be 16 bytes"), ); let b = u128::from_le_bytes( hash_bytes[16..32] .try_into() .expect("Chunk B must be 16 bytes"), ); let c = u128::from_le_bytes( hash_bytes[32..48] .try_into() .expect("Chunk C must be 16 bytes"), ); let d = u128::from_le_bytes( hash_bytes[48..64] .try_into() .expect("Chunk D must be 16 bytes"), ); a ^ b ^ c ^ d } pub async fn vrf_generate(self, wallet_key: String) -> VrfBlock { // Sign the unmined header hash with the miner wallet and derive // the VRF number from that signature. let hash = self.hash().await; let wallet = Wallet::try_obtain_wallet(wallet_key, None) .await .unwrap_or_else(|err| panic!("Wallet decryption failed: {err}")); let privkey = &wallet.saved.private_key; let proof = Wallet::sign_transaction(&hash, privkey).await; let vrf = Self::generate_random_number(&proof).await; VrfBlock { unmined_block: self, vrf, proof, } } // Hash the unmined block header for VRF signing. pub async fn hash(&self) -> String { let serialized = to_string(self).unwrap(); skein_256_hash_data(&serialized) } // Calculate the next difficulty using the rolling average and target block time. fn calculate_new_difficulty( current_difficulty: u64, difficulty_average: u64, average_duration: Duration, ) -> u64 { let lower_bound = Duration::from_secs(14); let upper_bound = Duration::from_secs(16); // When the rolling average is already within the target window, // use the cached mean difficulty exactly. if difficulty_average > 0 && average_duration >= lower_bound && average_duration <= upper_bound { return difficulty_average; } // Outside the target window, apply the capped 30% adjustment // with integer math to keep the result stable. let adjustment = current_difficulty.saturating_mul(30).saturating_div(100); if average_duration > upper_bound { current_difficulty.saturating_add(adjustment) } else if average_duration < lower_bound { current_difficulty.saturating_sub(adjustment) } else { current_difficulty } } // Adjust difficulty based on the latest saved block averages. pub async fn adjust_difficulty( current_timestamp: u32, db: &Db, current_difficulty: u64, ) -> u64 { let block_number = get_height(db); // Refresh rolling block data before reading averages. update_block_data(block_number).await; // Get the current rolling difficulty and duration averages. let (difficulty_average, average_duration) = calculate_averages(current_timestamp).await; // Apply the bounded difficulty adjustment. Self::calculate_new_difficulty(current_difficulty, difficulty_average, average_duration) } } impl VrfBlock { pub async fn hash(&self) -> String { // Hash the full VRF header for indexing and validation. let serialized = to_string(self).unwrap(); skein_256_hash_data(&serialized) } pub async fn to_bytes(&self) -> tokio::io::Result> { // Serialize the fixed-width VRF header layout. let mut buffer = Vec::with_capacity(VRF_BLOCK_BYTES); let mut cursor = Cursor::new(&mut buffer); cursor .write_all(&self.unmined_block.timestamp.to_le_bytes()) .await?; let miner_bytes = Wallet::short_address_to_bytes(&self.unmined_block.miner) .ok_or_else(|| tokio::io::Error::other("Invalid short miner address"))?; cursor.write_all(&miner_bytes).await?; cursor .write_all(&decode(&self.unmined_block.previous_hash).unwrap()) .await?; cursor .write_all(&self.unmined_block.next_block_difficulty.to_le_bytes()) .await?; cursor .write_all(&self.unmined_block.nonce.to_le_bytes()) .await?; cursor.write_all(&self.vrf.to_le_bytes()).await?; cursor.write_all(&decode(&self.proof).unwrap()).await?; Ok(buffer) } pub async fn from_bytes(bytes: &[u8]) -> tokio::io::Result { // A VRF header must be exactly the fixed header byte length. if bytes.len() != VRF_BLOCK_BYTES { return Err(tokio::io::Error::other("Invalid Byte Count for Block")); } // Read from the fixed-width VRF header bytes. let mut cursor = Cursor::new(bytes); // Decode timestamp and miner short address. let timestamp = cursor.read_u32_le().await?; let mut miner_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH]; cursor.read_exact(&mut miner_bytes).await?; let miner = Wallet::bytes_to_short_address(&miner_bytes) .ok_or_else(|| tokio::io::Error::other("Invalid short miner address"))?; // Decode parent hash, difficulty, nonce, VRF number, and proof. let mut prev_hash_bytes = vec![0; 32]; cursor.read_exact(&mut prev_hash_bytes).await?; let previous_hash = encode(&prev_hash_bytes); let next_block_difficulty = cursor.read_u64_le().await?; let nonce = cursor.read_u8().await?; let mut vrf_bytes = [0u8; 16]; cursor.read_exact(&mut vrf_bytes).await?; let vrf = u128::from_le_bytes(vrf_bytes); let mut proof_bytes = vec![0; Wallet::SIGNATURE_LENGTH]; cursor.read_exact(&mut proof_bytes).await?; let proof = encode(&proof_bytes); let unmined_block = UnminedBlock { timestamp, miner, previous_hash, next_block_difficulty, nonce, }; Ok(VrfBlock { unmined_block, vrf, proof, }) } } impl Block { pub async fn to_bytes(&self) -> tokio::io::Result> { let mut buffer = Vec::new(); // Serialize the fixed-width VRF header before any transactions. let vrf_bytes = self.vrf_block.to_bytes().await?; buffer.extend_from_slice(&vrf_bytes); // Append each transaction in block order using its own fixed layout. for transaction in &self.transactions { match transaction { Transaction::Genesis(genesis_tx) => { let tx_bytes = genesis_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Rewards(rewards_tx) => { let tx_bytes = rewards_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Transfer(transfer_tx) => { let tx_bytes = transfer_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Token(token_tx) => { let tx_bytes = token_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::IssueToken(issue_token_tx) => { let tx_bytes = issue_token_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Burn(burn_tx) => { let tx_bytes = burn_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Nft(nft_tx) => { let tx_bytes = nft_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Marketing(marketing_tx) => { let tx_bytes = marketing_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Swap(swap_tx) => { let tx_bytes = swap_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Lender(lender_tx) => { let tx_bytes = lender_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Borrower(borrower_tx) => { let tx_bytes = borrower_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Collateral(collateral_tx) => { let tx_bytes = collateral_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } Transaction::Vanity(vanity_tx) => { let tx_bytes = vanity_tx.to_bytes().await?; buffer.extend_from_slice(&tx_bytes); } } } Ok(buffer) } }