Skip to content

Commit 81e739b

Browse files
authored
Create blockchain_gui.py
1 parent 8e12020 commit 81e739b

1 file changed

Lines changed: 210 additions & 0 deletions

File tree

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import hashlib
2+
import json
3+
import os
4+
import time
5+
import uuid
6+
from dataclasses import dataclass, asdict
7+
from typing import List
8+
import tkinter as tk
9+
from tkinter import messagebox
10+
11+
import ttkbootstrap as tb
12+
from ttkbootstrap.constants import *
13+
from ttkbootstrap.scrolled import ScrolledText
14+
15+
# ---------------- CONFIG ---------------- #
16+
CHAIN_FILE = "blockchain.json"
17+
DIFFICULTY = 4
18+
MINING_REWARD = 50
19+
20+
# ---------------- DATA STRUCTURES ---------------- #
21+
@dataclass
22+
class Transaction:
23+
sender: str
24+
recipient: str
25+
amount: float
26+
timestamp: float
27+
28+
@dataclass
29+
class Block:
30+
index: int
31+
timestamp: float
32+
transactions: List[Transaction]
33+
previous_hash: str
34+
nonce: int = 0
35+
hash: str = ""
36+
37+
def compute_hash(self):
38+
data = {
39+
"index": self.index,
40+
"timestamp": self.timestamp,
41+
"transactions": [asdict(tx) for tx in self.transactions],
42+
"previous_hash": self.previous_hash,
43+
"nonce": self.nonce,
44+
}
45+
return hashlib.sha256(json.dumps(data, sort_keys=True).encode()).hexdigest()
46+
47+
# ---------------- BLOCKCHAIN ---------------- #
48+
class Blockchain:
49+
def __init__(self):
50+
self.chain: List[Block] = []
51+
self.pending: List[Transaction] = []
52+
self.load()
53+
54+
def create_genesis(self):
55+
block = Block(0, time.time(), [], "0")
56+
block.hash = block.compute_hash()
57+
self.chain.append(block)
58+
self.save()
59+
60+
def load(self):
61+
if not os.path.exists(CHAIN_FILE):
62+
self.create_genesis()
63+
return
64+
with open(CHAIN_FILE, "r") as f:
65+
data = json.load(f)
66+
self.chain = [self.dict_to_block(b) for b in data]
67+
68+
def save(self):
69+
with open(CHAIN_FILE, "w") as f:
70+
json.dump([self.block_to_dict(b) for b in self.chain], f, indent=2)
71+
72+
def add_transaction(self, sender, recipient, amount):
73+
self.pending.append(Transaction(sender, recipient, amount, time.time()))
74+
75+
def mine(self, miner):
76+
if not self.pending:
77+
return None
78+
79+
self.pending.append(Transaction("SYSTEM", miner, MINING_REWARD, time.time()))
80+
81+
block = Block(
82+
index=len(self.chain),
83+
timestamp=time.time(),
84+
transactions=self.pending.copy(),
85+
previous_hash=self.chain[-1].hash,
86+
)
87+
88+
while True:
89+
block.hash = block.compute_hash()
90+
if block.hash.startswith("0" * DIFFICULTY):
91+
break
92+
block.nonce += 1
93+
94+
self.chain.append(block)
95+
self.pending.clear()
96+
self.save()
97+
return block
98+
99+
def is_valid(self):
100+
for i in range(1, len(self.chain)):
101+
c, p = self.chain[i], self.chain[i - 1]
102+
if c.hash != c.compute_hash():
103+
return False
104+
if c.previous_hash != p.hash:
105+
return False
106+
return True
107+
108+
def balance(self, addr):
109+
bal = 0
110+
for b in self.chain:
111+
for t in b.transactions:
112+
if t.sender == addr:
113+
bal -= t.amount
114+
if t.recipient == addr:
115+
bal += t.amount
116+
return bal
117+
118+
def block_to_dict(self, b):
119+
return {
120+
"index": b.index,
121+
"timestamp": b.timestamp,
122+
"transactions": [asdict(t) for t in b.transactions],
123+
"previous_hash": b.previous_hash,
124+
"nonce": b.nonce,
125+
"hash": b.hash,
126+
}
127+
128+
def dict_to_block(self, d):
129+
txs = [Transaction(**t) for t in d["transactions"]]
130+
return Block(d["index"], d["timestamp"], txs, d["previous_hash"], d["nonce"], d["hash"])
131+
132+
# ---------------- GUI ---------------- #
133+
class BlockchainGUI:
134+
def __init__(self, app):
135+
self.app = app
136+
self.chain = Blockchain()
137+
self.wallet = uuid.uuid4().hex
138+
139+
self.build_ui()
140+
self.refresh()
141+
142+
def build_ui(self):
143+
self.app.title("🔗 Blockchain GUI")
144+
self.app.geometry("1000x700")
145+
146+
top = tb.Frame(self.app, padding=15)
147+
top.pack(fill=X)
148+
149+
tb.Label(top, text="🔗 Blockchain Explorer", font=("Segoe UI", 18, "bold")).pack(anchor=W)
150+
self.balance_lbl = tb.Label(top, text="")
151+
self.balance_lbl.pack(anchor=W, pady=5)
152+
tb.Label(top, text=f"👛 Wallet: {self.wallet}", font=("Segoe UI", 9)).pack(anchor=W)
153+
154+
form = tb.Labelframe(self.app, text="Create Transaction", padding=10)
155+
form.pack(fill=X, padx=15, pady=10)
156+
157+
self.to_var = tk.StringVar()
158+
self.amount_var = tk.DoubleVar()
159+
160+
tb.Entry(form, textvariable=self.to_var).pack(fill=X, pady=5)
161+
tb.Entry(form, textvariable=self.amount_var).pack(fill=X, pady=5)
162+
163+
tb.Button(form, text="Send Transaction", bootstyle=SUCCESS, command=self.send_tx).pack()
164+
165+
actions = tb.Frame(self.app, padding=10)
166+
actions.pack(fill=X)
167+
168+
tb.Button(actions, text="⛏ Mine Block", bootstyle=WARNING, command=self.mine).pack(side=LEFT, padx=5)
169+
tb.Button(actions, text="✔ Validate Chain", bootstyle=INFO, command=self.validate).pack(side=LEFT)
170+
171+
view = tb.Labelframe(self.app, text="Blockchain", padding=10)
172+
view.pack(fill=BOTH, expand=True, padx=15, pady=10)
173+
174+
self.text = ScrolledText(view)
175+
self.text.pack(fill=BOTH, expand=True)
176+
self.text.text.configure(state="disabled")
177+
178+
def refresh(self):
179+
self.balance_lbl.config(text=f"💰 Balance: {self.chain.balance(self.wallet)}")
180+
self.text.text.configure(state="normal")
181+
self.text.text.delete("1.0", END)
182+
for block in self.chain.chain:
183+
self.text.text.insert(END, json.dumps(self.chain.block_to_dict(block), indent=2))
184+
self.text.text.insert(END, "\n\n")
185+
self.text.text.configure(state="disabled")
186+
187+
def send_tx(self):
188+
if not self.to_var.get() or self.amount_var.get() <= 0:
189+
messagebox.showerror("Error", "Invalid transaction")
190+
return
191+
self.chain.add_transaction(self.wallet, self.to_var.get(), self.amount_var.get())
192+
messagebox.showinfo("Success", "Transaction added")
193+
194+
def mine(self):
195+
block = self.chain.mine(self.wallet)
196+
if not block:
197+
messagebox.showwarning("Mine", "No transactions")
198+
else:
199+
messagebox.showinfo("Mine", f"Block #{block.index} mined!")
200+
self.refresh()
201+
202+
def validate(self):
203+
ok = self.chain.is_valid()
204+
messagebox.showinfo("Validation", "Blockchain is valid ✔" if ok else "Blockchain corrupted ❌")
205+
206+
# ---------------- RUN ---------------- #
207+
if __name__ == "__main__":
208+
app = tb.Window(themename="darkly")
209+
BlockchainGUI(app)
210+
app.mainloop()

0 commit comments

Comments
 (0)