INFOB301 — University of Namur
Language: Rust 🦀
Purpose: Use blockchain and Proof-of-Work to reach consensus on a dancing stickman’s moves.
Project 2025 is a university assignment designed to implement a semi-decentralized blockchain system in Rust, with a quirky but educational twist: miners compete to build a dance sequence for a virtual stickman, where each block contributes a single dance move (Y, M, C, or A).
The system includes:
- A centralized server acting as the shared source of truth
- Multiple clients (miners) that independently build and extend the blockchain
- A blockchain tree, allowing forks and using the longest chain rule to determine consensus
Located in the miner/ directory.
Responsibilities:
- Mining new blocks using Proof-of-Work (PoW)
- Building the blockchain tree
- Interacting with the central server to fetch and submit blocks
Located in the server/ directory.
Responsibilities:
- Store all submitted blocks
- Validate basic correctness (PoW & uniqueness)
- Provide all known blocks to clients
Each block includes:
parent_hash: SHA256 hash of the parent block (empty for genesis)miner: Name of the miner that created the blocknonce: A random value used in PoWdancemove: Integer (1–4) representing the moves Y, M, C, A
- Must have an empty
parent_hash - Must have the miner name
"Genesis" - Must pass the PoW check
- Represented as a tree (not just a chain)
- Multiple branches (forks) allowed
- The longest valid chain determines the winning sequence
Miners must find a nonce so that the SHA256 hash of the block has the first N bits set to 0, where N is the difficulty (default is 25).
Why?
- Prevents spam
- Ensures fairness through computational effort
- Ensures only well-formed blocks are added
Where?
- Implemented in
solve_block(inblock.rs), using random nonces until the hash meets the difficulty
Each block's unique identifier is its SHA256 hash, computed from:
parent_hashminernoncedancemove
Purpose:
- Serves as a fingerprint for each block
- Enables block linking via
parent_hash
Where?
- Implemented in
hash_block(inblock.rs), using a top-down concatenation order
- Genesis blocks: Must have
parent_hash = "",miner = "Genesis", and valid PoW - Non-genesis blocks:
miner ≠ "Genesis"or"changemeyoufool"dancemove∈ {1, 2, 3, 4}- Must pass PoW
Why?
- Prevents invalid data from entering the blockchain
- Ensures dance moves are valid and blocks are unique
Where?
- Implemented in
is_block_validandis_genesis(inblock.rs)
The blockchain is represented as a tree using the TreeNode struct from simpletree.rs.
- Supports multiple forks
- Enables traversal and analysis
- Allows miners to build on forks
How?
- Each node = 1 block
- Children are blocks whose
parent_hashmatches the current block’s hash
Where?
- Blockchain logic is in
Blockchainstruct (inminer.rs) - Tree handling is provided by
TreeNode<Block>
- Accepts blocks:
POST /postblock - Returns all blocks:
GET /blocks - Performs basic validation: PoW & uniqueness
- Syncs with the server
- Builds a local blockchain tree
- Mines on the longest chain tip
- Submits mined blocks to the server
Why?
- Ensures consistency among all miners
- Prevents duplication
- Allows central authority to track global state
Where?
network.rshandles communication (viareqwest::blocking)- Integrated into mining loop in
miner.rs
The mining logic includes three concurrent threads:
| Thread | Role |
|---|---|
| Network | Periodically fetches new blocks from the server |
| Mining | Mines new blocks on the tip of the longest chain |
| Main | Updates blockchain and forwards tips to mining thread |
Steps:
- Fetch blocks
- Rebuild blockchain tree
- Find tip of longest chain
- Mine a new block
- Submit it to the server
Where?
- Implemented in
minefunction (inminer.rs)
At any time, the valid chain with the most blocks is considered the winner.
- Winning dance sequence = moves in the longest valid chain
- Forks can happen but only one chain will dominate
How?
find_longest_chain_tiprecursively searches for the deepest leaf
Why?
- Enforces consensus
- Encourages honest mining
Purpose: Defines block structure, hashing, validation, and PoW.
Key Structures:
Block: Struct with fields:parent_hash,miner,nonce,dancemoveDanceMove: Enum (Y=1, M=2, C=3, A=4)
Important Methods:
new(): Creates a new blockhash_block(): Computes SHA256 hash of the blocksolve_block(): Mines the block by finding a valid noncepow_check(): Verifies if a hash meets PoW difficultyis_block_valid(): Applies validation rulesis_genesis(): Checks if a block is a valid genesis blockis_parent(): Checks parent-child relation via hash
Design Choices:
- PoW check: Uses full-byte + bitwise logic for performance
- Random nonces: Generated using
RngCorefor PoW efficiency - Validation split: Genesis and non-genesis treated separately
Purpose: Defines blockchain structure, mining process, and CLI entry point.
Key Structures:
Blockchain: Holds aTreeNode<Block>to manage tree
Functions:
new_from_genesis(): Starts a blockchain from genesisnew_from_genesis_and_vec(): Reconstructs blockchain from blocksmine(): Handles mining with three threadsfind_longest_chain_tip(): Finds the deepest leaf nodemain(): CLI interface with two commands:mine: Starts miningprint: Displays blockchain
Design Choices:
- Threading: Uses
std::sync::mpscchannels for safe communication - Genesis strategy: Creates or selects a genesis block from server
- Chain updates: Iteratively inserts blocks, rechecking orphans
- Visual output: Tree printed using
print_tree()
Purpose: Handles communication with the server.
Key Structure:
NetworkConnector: Uses channels to send/receive blocks
Key Methods:
sync(): Periodically pulls/pushes blocks from/to serverget_blocks(): Fetches all blocks viaGET /blocks
Design Choices:
- Blocking networking: Uses
reqwest::blockingfor simplicity - Rate limiting: 1 request/sec to comply with project rules
- Fault tolerance: Continues running on network failure
Purpose: Provides generic tree structure for blockchain.
Key Elements:
TreeNode<T>: Node with childrenParentingtrait: Lets nodes identify valid parents
Methods:
insert,depth,look_for_parent, etc.
Design Choices:
- DFS lookup: Efficient parent matching
- Reusability: Can be applied to any structure implementing
Parenting
Purpose:
Exposes block and simpletree modules for use in miner.rs
Minimal glue code
- Improves module organization
Lists all dependencies:
serde = "1.0"
serde_derive = "1.0"
rand = "0.8"
sha2 = "0.10"
clap = "3.2"
reqwest = { version = "0.11", features = ["blocking", "json"] }Purpose: Implements centralized HTTP server with basic block validation.
Key Logic:
- Uses
rouillefor HTTP endpoints:GET /blocks: Returns all stored blocksPOST /postblock: Accepts a new block
Storage:
- In-memory
HashMapusingnonceas key
Validation:
- Verifies PoW
- Rejects duplicate nonces
rouille = "3.0"
clap = "3.2"
serde = { version = "1.0", features = ["derive"] }Run unit tests:
cd miner
cargo testTests cover:
- Genesis block validation
- PoW edge cases
- Blockchain reconstruction
- Forks, orphans, and duplicates
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shcd server
cargo runOptional:
cargo run -- -a 127.0.0.1 -p 8080 -d 10cd miner
cargo run -- mine -d 10 -m YourMinerNamePrint the blockchain:
cargo run -- print -d 10- Async networking (via
tokio) - Protocol attacks for bonus marks
- Improved multiple genesis handling
- Persistent storage on the server
This project is intended only for educational purposes and not for production.
Use at your own risk.
Created as part of INFOB301 at Université de Namur
All logic and implementation decisions align with course specifications.