Contractless/src/blocks/rewards.rs

89 lines
2.9 KiB
Rust

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<Vec<u8>> {
// 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<Self> {
// 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 })
}
}