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::asert_genesis_anchor; use crate::sled::Db; use crate::to_string; use crate::wallets::structures::Wallet; use crate::Cursor; use crate::Serialize; use crate::{decode, encode}; use crate::{AsyncReadExt, AsyncWriteExt}; const TARGET_BLOCK_SECONDS: i128 = 15; const ASERT_HALF_LIFE_SECONDS: i128 = 300; const ASERT_RADIX_BITS: i128 = 16; const ASERT_FIXED_ONE: i128 = 1 << ASERT_RADIX_BITS; 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, private_key: &str) -> VrfBlock { // Sign the unmined header hash with the miner wallet and derive // the VRF number from that signature. let hash = self.hash().await; let proof = Wallet::sign_transaction(&hash, private_key).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) } fn asert_target(anchor_target: u64, height_delta: u32, time_delta: i128) -> u64 { // Deterministic fixed-point ASERT calculation. The polynomial is the // BCH ASERT approximation for 2^x, avoiding platform-dependent floats. let expected_time = height_delta as i128 * TARGET_BLOCK_SECONDS; let time_error = time_delta - expected_time; let exponent = (time_error << ASERT_RADIX_BITS) / ASERT_HALF_LIFE_SECONDS; let shifts = exponent >> ASERT_RADIX_BITS; let frac = exponent - (shifts << ASERT_RADIX_BITS); let factor = ASERT_FIXED_ONE + ((195_766_423_245_049_i128 * frac + 971_821_376_i128 * frac * frac + 5_127_i128 * frac * frac * frac + (1_i128 << 47)) >> 48); let mut target = anchor_target as u128 * factor.max(1) as u128; if shifts >= 0 { if shifts >= 64 { return u64::MAX; } target = target.checked_shl(shifts as u32).unwrap_or(u128::MAX); } else { let right_shift = (-shifts) as u32; if right_shift >= 128 { return 1; } target >>= right_shift; } target >>= ASERT_RADIX_BITS as u32; target.clamp(1, u64::MAX as u128) as u64 } fn clamp_per_block(raw_target: u64, current_difficulty: u64) -> u64 { // ASERT provides the direction and scale, while this guard keeps any // single block from swinging the threshold too far. let lower_bound = current_difficulty .saturating_mul(85) .saturating_div(100) .max(1); let upper_bound = current_difficulty .saturating_mul(115) .saturating_div(100) .max(lower_bound); raw_target.clamp(lower_bound, upper_bound) } // Adjust difficulty based on ASERT drift from the genesis anchor. pub async fn adjust_difficulty( current_timestamp: u32, db: &Db, current_difficulty: u64, ) -> u64 { let block_number = get_height(db); let candidate_height = block_number + 1; let Some((anchor_height, anchor_timestamp, anchor_difficulty)) = asert_genesis_anchor().await else { return current_difficulty; }; if anchor_height >= candidate_height { return current_difficulty; } let height_delta = candidate_height - anchor_height; let time_delta = current_timestamp as i128 - anchor_timestamp as i128; let raw_target = Self::asert_target(anchor_difficulty, height_delta, time_delta); Self::clamp_per_block(raw_target, current_difficulty) } } 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) } }