blockchain-core/core/src/block.rs
StillHammer 64ea897cdc Initial setup: Rust workspace with core lib + axum node
- Workspace: core/ (blockchain library) + node/ (REST API)
- Core: block, chain, wallet, transaction, mining, persistence, state
- Node: axum 0.8 REST API with full endpoint set
- SHA-256 hashing, Ed25519 signatures, account-based model
- Unit tests for all core modules
2026-02-01 10:12:27 +08:00

95 lines
2.7 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::transaction::Transaction;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockHeader {
pub index: u64,
pub timestamp: DateTime<Utc>,
pub previous_hash: String,
pub hash: String,
pub nonce: u64,
pub difficulty: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Block {
pub header: BlockHeader,
pub transactions: Vec<Transaction>,
}
impl Block {
/// Create a new block (hash is computed, nonce is 0 until mined).
pub fn new(index: u64, previous_hash: String, transactions: Vec<Transaction>, difficulty: u32) -> Self {
let mut block = Block {
header: BlockHeader {
index,
timestamp: Utc::now(),
previous_hash,
hash: String::new(),
nonce: 0,
difficulty,
},
transactions,
};
block.header.hash = block.calculate_hash();
block
}
/// Create the genesis block.
pub fn genesis() -> Self {
Block::new(0, crate::config::GENESIS_PREV_HASH.to_string(), vec![], crate::config::INITIAL_DIFFICULTY)
}
/// Calculate SHA-256 hash of the block.
pub fn calculate_hash(&self) -> String {
let data = format!(
"{}{}{}{}{}",
self.header.index,
self.header.timestamp.timestamp_millis(),
self.header.previous_hash,
self.header.nonce,
serde_json::to_string(&self.transactions).unwrap_or_default(),
);
let mut hasher = Sha256::new();
hasher.update(data.as_bytes());
hex::encode(hasher.finalize())
}
/// Check if the block hash satisfies the difficulty target.
pub fn hash_meets_difficulty(&self) -> bool {
let prefix = "0".repeat(self.header.difficulty as usize);
self.header.hash.starts_with(&prefix)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_genesis_block() {
let genesis = Block::genesis();
assert_eq!(genesis.header.index, 0);
assert!(genesis.transactions.is_empty());
assert!(!genesis.header.hash.is_empty());
}
#[test]
fn test_calculate_hash_deterministic() {
let block = Block::genesis();
let hash1 = block.calculate_hash();
let hash2 = block.calculate_hash();
assert_eq!(hash1, hash2);
}
#[test]
fn test_hash_is_hex_64_chars() {
let block = Block::genesis();
assert_eq!(block.header.hash.len(), 64);
assert!(block.header.hash.chars().all(|c| c.is_ascii_hexdigit()));
}
}