use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Transaction {
pub raw_bytes: Vec<u8>,
pub tx_index: u32,
pub gas_used: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BlockHeader {
pub parent_id: [u8; 32],
pub height: u64,
pub timestamp: u64,
pub tx_count: u32,
pub state_root: [u8; 32],
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Block {
pub header: BlockHeader,
pub transactions: Vec<Transaction>,
}
impl Block {
pub fn new(
parent_id: [u8; 32],
height: u64,
timestamp: u64,
transactions: Vec<Transaction>,
state_root: [u8; 32],
) -> Self {
let header = BlockHeader {
parent_id,
height,
timestamp,
tx_count: transactions.len() as u32,
state_root,
};
Self {
header,
transactions,
}
}
pub fn genesis(chain_id: u64, timestamp: u64) -> Self {
let mut parent = [0u8; 32];
parent[..8].copy_from_slice(&chain_id.to_le_bytes());
Self::new(parent, 0, timestamp, Vec::new(), [0u8; 32])
}
pub fn id(&self) -> [u8; 32] {
let header_bytes = serde_json::to_vec(&self.header).unwrap_or_default();
*blake3::hash(&header_bytes).as_bytes()
}
pub fn to_bytes(&self) -> Vec<u8> {
serde_json::to_vec(self).unwrap_or_default()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, serde_json::Error> {
serde_json::from_slice(bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn genesis_block_has_height_zero() {
let block = Block::genesis(31337, 1700000000);
assert_eq!(block.header.height, 0);
assert!(block.transactions.is_empty());
}
#[test]
fn block_id_is_deterministic() {
let block = Block::genesis(31337, 1700000000);
assert_eq!(block.id(), block.id());
}
#[test]
fn different_blocks_have_different_ids() {
let a = Block::genesis(31337, 1700000000);
let b = Block::genesis(31337, 1700000001);
assert_ne!(a.id(), b.id());
}
#[test]
fn serialization_round_trip() {
let block = Block::new(
[0xaa; 32],
10,
1700000000,
vec![Transaction {
raw_bytes: vec![0xde, 0xad],
tx_index: 0,
gas_used: 21000,
}],
[0xbb; 32],
);
let bytes = block.to_bytes();
let parsed = Block::from_bytes(&bytes).unwrap();
assert_eq!(block, parsed);
}
#[test]
fn block_id_is_blake3_of_header() {
let block = Block::genesis(1, 0);
let header_bytes = serde_json::to_vec(&block.header).unwrap();
let expected = *blake3::hash(&header_bytes).as_bytes();
assert_eq!(block.id(), expected);
}
}