Contractless/src/blocks/genesis.rs

83 lines
2.8 KiB
Rust

use crate::common::binary_conversions::binary_to_string;
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)] // 49 bytes
pub struct UnsignedGenesisTransaction {
pub txtype: u8, // 1 byte transaction type, should be 0
pub message: String, // 48 bytes fixed genesis block message
}
#[derive(Debug, Serialize, Clone)] // 49 bytes
pub struct GenesisTransaction {
pub unsigned: UnsignedGenesisTransaction, // 49 bytes
}
impl UnsignedGenesisTransaction {
// Create the unsigned genesis transaction.
pub async fn new(txtype: u8, message: &str) -> Self {
Self {
txtype,
message: message.to_string(),
}
}
pub async fn hash(&self) -> String {
// Genesis hashes the serialized unsigned payload like other
// transaction types, even though it has no wallet signature.
let serialized = to_string(self).unwrap();
skein_256_hash_data(&serialized)
}
}
impl GenesisTransaction {
const MESSAGE_LENGTH: usize = 48;
pub const BYTE_LENGTH: usize = 1 + Self::MESSAGE_LENGTH;
pub async fn new(unsigned: UnsignedGenesisTransaction) -> Self {
// Wrap a freshly created unsigned genesis payload.
Self { unsigned }
}
pub async fn load(unsigned: UnsignedGenesisTransaction) -> Self {
// Rebuild a genesis transaction already read from storage.
Self { unsigned }
}
pub async fn to_bytes(&self) -> tokio::io::Result<Vec<u8>> {
// Serialize into the fixed genesis byte layout:
// type byte followed by the fixed-length launch message.
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.message.as_bytes()).await?;
Ok(buffer)
}
pub async fn from_bytes(txtype: u8, bytes: &[u8]) -> tokio::io::Result<Self> {
// The transaction type is read by the block parser, so this
// function receives only the remaining genesis bytes.
if bytes.len() != Self::BYTE_LENGTH - 1 {
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read from the remaining fixed-width transaction bytes.
let mut cursor = Cursor::new(bytes);
// Decode the fixed 48-byte genesis message.
let mut message_bytes = vec![0; Self::MESSAGE_LENGTH];
cursor.read_exact(&mut message_bytes).await?;
let message = binary_to_string(message_bytes);
let unsigned = UnsignedGenesisTransaction { txtype, message };
Ok(GenesisTransaction { unsigned })
}
}