A multi-client blockchain simulator built in C demonstrating core OS concepts through a realistic blockchain-inspired architecture.
| Concept | Where |
|---|---|
| Role-based authorization | auth.c — guest/user/miner roles, action-gated |
| File locking | ledger.c — fcntl F_RDLCK / F_WRLCK on ledger.dat |
| Concurrency control | miner.c, mempool.c — pthreads, mutex, semaphores |
| Data consistency | ledger.c — append-only ledger, balance recomputed from chain |
| Socket programming | server.c, client.c — TCP multi-client server |
| IPC | mempool.c, handler.c — named pipe (FIFO) between handler threads and mempool manager |
make # builds both server and client
make clean # removes binaries and data filesRequirements: GCC, POSIX (Linux/macOS), pthreads
Terminal 1 — Server:
./server/server <port> <miners> <block_size>
# example: port 8891, 3 miners, block fills at 3 transactions
./server/server 8891 3 3Terminal 2+ — Client(s):
./client/client <server_ip> <port>
# example:
./client/client 127.0.0.1 88911. Register — create account (role: guest / user / miner)
2. Login — authenticate
3. Submit tx — send coins to another wallet (user/miner only)
4. View balance — check your wallet balance (user/miner only)
5. View blockchain — print all blocks and transactions (everyone)
6. Verify tx by ID — confirm a transaction is in a block (everyone)
7. Logout
8. View mempool — pending tx count + active miner (miner only)
0. Exit
Clients ──TCP sockets──► Auth module
│
Client handler threads (1 per connection)
│ │
FIFO (IPC) fcntl RDLCK
│ │
Mempool manager ledger.dat
│
sem_post (race / round-robin)
│
┌──────────┼──────────┐
Miner 0 Miner 1 Miner N
└──────────┼──────────┘
fcntl WRLCK
│
ledger.dat (append-only)
- Race on empty mempool: when first transaction arrives, all miners race (mutex). Winner becomes active.
- Hybrid trigger: active miner pops up to N txs. Starts a 15s timer.
- New txs arriving are absorbed into the block (up to N) — mines immediately when full.
- If 15s expires before N txs — mines with whatever it has.
- Round-robin handoff: after mining, if mempool still has txs, baton passes to next miner (no race). If empty — all sleep.
data/ledger.dat — one block per line:
<index>|<prev_hash>|<hash>|<timestamp>|<miner_id>|<tx_count>|<tx_id>,<from>,<to>,<amount>,<ts>|...
data/users.dat — one user per line:
<username>|<password_hash>|<wallet_addr>|<role>
blockchain_sim/
├── server/
│ ├── server.c main, socket accept loop, global state
│ ├── state.h all shared structs and constants
│ ├── auth.c/h login, register, role permission checks
│ ├── mempool.c/h circular queue, FIFO reader thread
│ ├── miner.c/h hybrid miner lifecycle, round-robin handoff
│ ├── ledger.c/h append-only ledger with fcntl locking
│ ├── handler.c/h client handler thread, protocol parser
│ └── hash.c/h djb2 hashing for wallets, tx IDs, blocks
├── client/
│ └── client.c TCP client with CLI menu
├── data/ created at runtime
│ ├── ledger.dat blockchain ledger
│ └── users.dat registered users
├── Makefile
└── README.md