use crate::primitives::{Address, BlockHeight, Hash, Timestamp};
use sha2::{Digest, Sha256};
use crate::transaction::SignedTransaction;
use serde::{Deserialize, Serialize};
pub const MAX_TRANSACTIONS_PER_BLOCK: usize = 10_000;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BlockHeader {
pub height: BlockHeight,
#[serde(default)]
pub view: u64,
pub prev_hash: Hash,
pub tx_root: Hash,
pub state_root: Hash,
pub timestamp: Timestamp,
pub proposer: Address,
pub consensus_proof: ConsensusProof,
pub metadata: BlockMetadata,
}
impl BlockHeader {
pub fn new(
height: BlockHeight,
prev_hash: Hash,
tx_root: Hash,
state_root: Hash,
proposer: Address,
consensus_proof: ConsensusProof,
) -> Self {
Self::new_at_view(height, 0, prev_hash, tx_root, state_root, proposer, consensus_proof)
}
pub fn new_at_view(
height: BlockHeight,
view: u64,
prev_hash: Hash,
tx_root: Hash,
state_root: Hash,
proposer: Address,
consensus_proof: ConsensusProof,
) -> Self {
Self {
height,
view,
prev_hash,
tx_root,
state_root,
timestamp: Timestamp::now(),
proposer,
consensus_proof,
metadata: BlockMetadata::default(),
}
}
pub fn hash(&self) -> Hash {
let mut hasher = Sha256::new();
hasher.update(self.height.0.to_le_bytes());
hasher.update(self.view.to_le_bytes());
hasher.update(self.prev_hash.0);
hasher.update(self.tx_root.0);
hasher.update(self.state_root.0);
hasher.update(self.timestamp.0.to_le_bytes());
hasher.update(self.proposer.0);
hasher.update(self.metadata.gas_used.to_le_bytes());
hasher.update(self.metadata.gas_limit.to_le_bytes());
hasher.update(self.metadata.tx_count.to_le_bytes());
hasher.update((self.metadata.protocol_version as u64).to_le_bytes());
match self.metadata.base_fee_per_gas {
None => hasher.update([0u8]),
Some(bf) => {
hasher.update([1u8]);
hasher.update(bf.to_le_bytes());
}
}
Hash::new(hasher.finalize().into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FeeMarketParams {
pub initial_base_fee: u128,
pub target_gas_per_block: u64,
pub base_fee_change_denominator: u64,
pub min_base_fee: u128,
pub max_base_fee: u128,
}
impl Default for FeeMarketParams {
fn default() -> Self {
Self {
initial_base_fee: 1_000_000_000,
target_gas_per_block: 15_000_000,
base_fee_change_denominator: 8,
min_base_fee: 100_000_000,
max_base_fee: 1_000_000_000_000,
}
}
}
pub fn calculate_next_base_fee(
parent_base_fee: Option<u128>,
parent_gas_used: u64,
parent_gas_limit: u64,
params: &FeeMarketParams,
) -> u128 {
let current = match parent_base_fee {
Some(bf) if parent_gas_limit > 0 => bf,
_ => return params.initial_base_fee,
};
let target = params.target_gas_per_block;
let denom = params.base_fee_change_denominator as u128;
let next_fee = if parent_gas_used == target {
current
} else if parent_gas_used > target {
let gas_delta = (parent_gas_used - target) as u128;
let fee_delta = (current * gas_delta) / (target as u128) / denom;
current + fee_delta.max(1)
} else {
let gas_delta = (target - parent_gas_used) as u128;
let fee_delta = (current * gas_delta) / (target as u128) / denom;
current.saturating_sub(fee_delta)
};
next_fee.clamp(params.min_base_fee, params.max_base_fee)
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct BlockMetadata {
pub gas_used: u64,
pub gas_limit: u64,
pub tx_count: u64,
pub protocol_version: u32,
pub base_fee_per_gas: Option<u128>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConsensusProof {
pub algorithm: ConsensusAlgorithm,
pub proof_data: Vec<u8>,
pub signatures: Vec<ValidatorSignature>,
}
impl ConsensusProof {
pub fn new(algorithm: ConsensusAlgorithm, proof_data: Vec<u8>) -> Self {
Self {
algorithm,
proof_data,
signatures: Vec::new(),
}
}
pub fn add_signature(&mut self, signature: ValidatorSignature) {
self.signatures.push(signature);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConsensusAlgorithm {
PoS,
PBFT,
Tendermint,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ValidatorSignature {
pub validator: Address,
pub signature: Vec<u8>,
pub voting_power: u128,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Block {
pub header: BlockHeader,
#[serde(deserialize_with = "crate::validation::bounded_tx_vec")]
pub transactions: Vec<SignedTransaction>,
}
impl Block {
pub fn new(header: BlockHeader, transactions: Vec<SignedTransaction>) -> Self {
Self {
header,
transactions,
}
}
pub fn new_validated(header: BlockHeader, transactions: Vec<SignedTransaction>) -> Result<Self, crate::error::TenzroError> {
if transactions.len() > MAX_TRANSACTIONS_PER_BLOCK {
return Err(crate::error::TenzroError::InvalidBlock(format!(
"Too many transactions: {} exceeds maximum of {}",
transactions.len(),
MAX_TRANSACTIONS_PER_BLOCK
)));
}
Ok(Self {
header,
transactions,
})
}
pub fn hash(&self) -> Hash {
self.header.hash()
}
pub fn height(&self) -> BlockHeight {
self.header.height
}
pub fn timestamp(&self) -> Timestamp {
self.header.timestamp
}
pub fn tx_count(&self) -> usize {
self.transactions.len()
}
pub fn proposer(&self) -> Address {
self.header.proposer
}
pub fn validate_structure(&self) -> bool {
self.transactions.len() as u64 == self.header.metadata.tx_count
}
}