use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use crate::{BlockHeight, Hash, SignedTransaction, Timestamp};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BlockHeader {
pub parent_hash: Hash,
pub height: BlockHeight,
pub timestamp: Timestamp,
pub tx_root: Hash,
pub state_root: Hash,
pub proposer_pubkey: [u8; 32],
#[serde(with = "BigArray")]
pub proposer_sig: [u8; 64],
}
impl BlockHeader {
pub fn new(
parent_hash: Hash,
height: BlockHeight,
timestamp: Timestamp,
tx_root: Hash,
state_root: Hash,
proposer_pubkey: [u8; 32],
) -> Self {
Self {
parent_hash,
height,
timestamp,
tx_root,
state_root,
proposer_pubkey,
proposer_sig: [0u8; 64], }
}
pub fn signing_hash(&self) -> Hash {
let signable = SignableHeader {
parent_hash: self.parent_hash,
height: self.height,
timestamp: self.timestamp,
tx_root: self.tx_root,
state_root: self.state_root,
proposer_pubkey: self.proposer_pubkey,
};
let bytes = bincode::serialize(&signable).expect("Header serialization should not fail");
Hash::hash(&bytes)
}
pub fn hash(&self) -> Hash {
let bytes = bincode::serialize(self).expect("Header serialization should not fail");
Hash::hash(&bytes)
}
pub fn is_genesis(&self) -> bool {
self.height == 0 && self.parent_hash.is_zero()
}
pub fn set_signature(&mut self, signature: [u8; 64]) {
self.proposer_sig = signature;
}
}
#[derive(Serialize)]
struct SignableHeader {
parent_hash: Hash,
height: BlockHeight,
timestamp: Timestamp,
tx_root: Hash,
state_root: Hash,
proposer_pubkey: [u8; 32],
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Block {
pub header: BlockHeader,
pub transactions: Vec<SignedTransaction>,
}
impl Block {
pub fn new(header: BlockHeader, transactions: Vec<SignedTransaction>) -> Self {
Self {
header,
transactions,
}
}
pub fn genesis(state_root: Hash, proposer_pubkey: [u8; 32], timestamp: Timestamp) -> Self {
let header = BlockHeader::new(
Hash::ZERO,
0,
timestamp,
Hash::ZERO, state_root,
proposer_pubkey,
);
Self {
header,
transactions: Vec::new(),
}
}
pub fn hash(&self) -> Hash {
self.header.hash()
}
pub fn height(&self) -> BlockHeight {
self.header.height
}
pub fn compute_tx_root(&self) -> Hash {
if self.transactions.is_empty() {
return Hash::ZERO;
}
let tx_hashes: Vec<Hash> = self.transactions.iter().map(|tx| tx.hash()).collect();
Hash::merkle_root(&tx_hashes)
}
pub fn verify_tx_root(&self) -> bool {
self.header.tx_root == self.compute_tx_root()
}
pub fn to_bytes(&self) -> Vec<u8> {
bincode::serialize(self).expect("Block serialization should not fail")
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, bincode::Error> {
bincode::deserialize(bytes)
}
pub fn tx_count(&self) -> usize {
self.transactions.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_header() -> BlockHeader {
BlockHeader::new(
Hash::ZERO,
1,
1000,
Hash::ZERO,
Hash::hash(b"state"),
[0u8; 32],
)
}
#[test]
fn test_signing_hash_excludes_signature() {
let mut header1 = sample_header();
let mut header2 = sample_header();
header1.proposer_sig = [1u8; 64];
header2.proposer_sig = [2u8; 64];
assert_eq!(header1.signing_hash(), header2.signing_hash());
}
#[test]
fn test_block_hash_includes_signature() {
let mut header1 = sample_header();
let mut header2 = sample_header();
header1.proposer_sig = [1u8; 64];
header2.proposer_sig = [2u8; 64];
assert_ne!(header1.hash(), header2.hash());
}
#[test]
fn test_genesis_block() {
let genesis = Block::genesis(Hash::hash(b"genesis_state"), [0u8; 32], 0);
assert!(genesis.header.is_genesis());
assert_eq!(genesis.height(), 0);
assert!(genesis.transactions.is_empty());
}
#[test]
fn test_tx_root_empty() {
let block = Block::new(sample_header(), vec![]);
assert_eq!(block.compute_tx_root(), Hash::ZERO);
}
#[test]
fn test_serialization_roundtrip() {
let block = Block::genesis(Hash::hash(b"state"), [42u8; 32], 12345);
let bytes = block.to_bytes();
let block2 = Block::from_bytes(&bytes).unwrap();
assert_eq!(block, block2);
}
}