use crate::common::skein::skein_256_hash_data; use crate::to_string; use crate::Cursor; use crate::Serialize; use crate::{AsyncReadExt, AsyncWriteExt}; #[derive(Debug, Serialize, Clone)] // 13 bytes pub struct UnsignedRewardsTransaction { pub txtype: u8, // 1 byte transaction type, should be 1 pub timestamp: u32, // 4 bytes timestamp used in the reward payload pub value: u64, // 8 bytes reward value } #[derive(Debug, Serialize, Clone)] pub struct RewardsTransaction { // 13 bytes pub unsigned: UnsignedRewardsTransaction, // 13 bytes consensus-created reward data } impl UnsignedRewardsTransaction { // Create an unsigned reward transaction for a mined block. pub async fn new(txtype: u8, timestamp: u32, value: u64) -> Self { Self { txtype, timestamp, value, } } // Hash the unsigned reward data for transaction indexing. pub async fn hash(&self) -> String { let serialized = to_string(self).unwrap(); skein_256_hash_data(&serialized) } } impl RewardsTransaction { pub const BYTE_LENGTH: usize = 1 + 4 + 8; pub async fn new(unsigned: UnsignedRewardsTransaction) -> Self { // Rewards are not wallet-signed, so the final transaction // only wraps the consensus-created unsigned data. Self { unsigned } } pub async fn load(unsigned: UnsignedRewardsTransaction) -> Self { // Rebuild a reward transaction already read from storage. Self { unsigned } } pub async fn to_bytes(&self) -> tokio::io::Result> { // Serialize into the fixed reward layout: type, timestamp, value. let mut buffer = Vec::with_capacity(Self::BYTE_LENGTH); let mut cursor = Cursor::new(&mut buffer); cursor .write_all(&self.unsigned.txtype.to_le_bytes()) .await?; cursor .write_all(&self.unsigned.timestamp.to_le_bytes()) .await?; cursor.write_all(&self.unsigned.value.to_le_bytes()).await?; Ok(buffer) } pub async fn from_bytes(txtype: u8, bytes: &[u8]) -> tokio::io::Result { // The block parser already consumed the transaction type byte. if bytes.len() != Self::BYTE_LENGTH - 1 { return Err(tokio::io::Error::other("Invalid Byte Count")); } // Read the remaining fixed reward bytes. let mut cursor = Cursor::new(bytes); // Decode timestamp and reward value in the same order they were written. let timestamp = cursor.read_u32_le().await?; let value = cursor.read_u64_le().await?; // Rebuild the unsigned reward payload. let unsigned = UnsignedRewardsTransaction { txtype, timestamp, value, }; // Wrap the rebuilt unsigned reward payload. Ok(RewardsTransaction { unsigned }) } }