1
1
import sys
2
2
import hashlib
3
3
import secrets
4
+ import random
5
+ import networkx as nx
4
6
from PyQt5 .QtWidgets import (
5
7
QApplication , QWidget , QVBoxLayout , QPushButton , QLabel , QLineEdit ,
6
- QMessageBox , QHBoxLayout , QTextEdit
8
+ QMessageBox , QHBoxLayout , QTextEdit , QGraphicsEllipseItem ,
9
+ QGraphicsTextItem , QColorDialog , QComboBox , QGraphicsScene , QGraphicsView ,QDialog , QDialogButtonBox
7
10
)
8
- from PyQt5 .QtGui import QFont
9
- from PyQt5 .QtCore import Qt
11
+ from PyQt5 .QtGui import QFont , QBrush , QColor , QPen
12
+ from PyQt5 .QtCore import Qt , QRectF
10
13
11
- class CommitmentGameWindow (QWidget ):
14
+ class CommitmentGame (QWidget ):
12
15
def __init__ (self ):
13
16
super ().__init__ ()
17
+ self .setWindowTitle ("ZKP Commitment Game - Master Edition" )
18
+ self .setGeometry (100 , 100 , 1200 , 700 )
14
19
15
- self .setWindowTitle ("🔒 Commitment Game" )
16
- self .setFixedSize (500 , 500 )
17
- self .setStyleSheet ("background-color: #2C3E50;" )
20
+ self .graph = nx .cycle_graph (5 )
21
+ self .colors = {}
22
+ self .commitments = {}
23
+ self .nonces = {}
24
+ self .revealed = set ()
18
25
19
- layout = QVBoxLayout ()
26
+ self .init_ui ()
27
+ self .draw_graph ()
28
+
29
+ def init_ui (self ):
30
+ layout = QHBoxLayout ()
31
+
32
+ self .scene = QGraphicsScene ()
33
+ self .view = QGraphicsView (self .scene )
34
+ layout .addWidget (self .view , 70 )
35
+
36
+ control_panel = QVBoxLayout ()
37
+
38
+ self .commit_button = QPushButton ("🔒 Commit to Colors" )
39
+ self .commit_button .clicked .connect (self .commit_colors )
40
+ control_panel .addWidget (self .commit_button )
41
+
42
+ self .challenge_button = QPushButton ("🎯 Verifier: Challenge Random Edge" )
43
+ self .challenge_button .clicked .connect (self .challenge_edge )
44
+ control_panel .addWidget (self .challenge_button )
45
+
46
+ self .cheat_button = QPushButton ("😈 Try to Cheat (Change Color)" )
47
+ self .cheat_button .clicked .connect (self .try_to_cheat )
48
+ control_panel .addWidget (self .cheat_button )
20
49
21
- title = QLabel ("🔐 Commitment Scheme Simulation" )
22
- title .setFont (QFont ("Arial" , 16 , QFont .Bold ))
23
- title .setStyleSheet ("color: white; padding: 10px;" )
24
- title .setAlignment (Qt .AlignCenter )
25
- layout .addWidget (title )
26
-
27
- role_label = QLabel ("🧑💻 You are the Prover. The system plays the Verifier." )
28
- role_label .setStyleSheet ("color: #ECF0F1; padding: 5px;" )
29
- role_label .setAlignment (Qt .AlignCenter )
30
- layout .addWidget (role_label )
31
-
32
- self .secret_input = QLineEdit ()
33
- self .secret_input .setPlaceholderText ("Enter your secret value" )
34
- self .secret_input .setFont (QFont ("Arial" , 12 ))
35
- self .secret_input .setStyleSheet ("padding: 8px;" )
36
- layout .addWidget (self .secret_input )
37
-
38
- commit_btn = QPushButton ("🔒 Commit to Secret" )
39
- commit_btn .setFont (QFont ("Arial" , 12 ))
40
- commit_btn .setStyleSheet ("margin: 10px; padding: 10px;" )
41
- commit_btn .clicked .connect (self .generate_commitment )
42
- layout .addWidget (commit_btn )
43
-
44
- reveal_btn = QPushButton ("🔓 Reveal to Verifier" )
45
- reveal_btn .setFont (QFont ("Arial" , 12 ))
46
- reveal_btn .setStyleSheet ("margin: 10px; padding: 10px;" )
47
- reveal_btn .clicked .connect (self .reveal_secret )
48
- layout .addWidget (reveal_btn )
49
-
50
- self .commitment_label = QLabel ("Commitment: ..." )
51
- self .commitment_label .setStyleSheet ("color: #ECF0F1; margin: 10px;" )
52
- layout .addWidget (self .commitment_label )
53
-
54
- self .transcript_box = QTextEdit ()
55
- self .transcript_box .setReadOnly (True )
56
- self .transcript_box .setStyleSheet ("background-color: #34495E; color: #F1C40F; padding: 10px;" )
57
- layout .addWidget (self .transcript_box )
58
-
59
- self .result_label = QLabel ("" )
60
- self .result_label .setStyleSheet ("color: #1ABC9C; font-weight: bold; padding: 10px;" )
61
- layout .addWidget (self .result_label )
50
+ self .education_button = QPushButton ("🧠 What’s Happening?" )
51
+ self .education_button .clicked .connect (self .show_education_modal )
52
+ control_panel .addWidget (self .education_button )
62
53
54
+ self .color_select = QComboBox ()
55
+ self .color_select .addItems (["Red" , "Green" , "Blue" ])
56
+ control_panel .addWidget (QLabel ("Choose color for next node click:" ))
57
+ control_panel .addWidget (self .color_select )
58
+
59
+ self .status = QLabel ("🔐 Set colors by clicking nodes. Then commit." )
60
+ self .status .setWordWrap (True )
61
+ control_panel .addWidget (self .status )
62
+
63
+ self .transcript = QTextEdit ()
64
+ self .transcript .setReadOnly (True )
65
+ self .transcript .setFont (QFont ("Courier" , 10 ))
66
+ control_panel .addWidget (QLabel ("📝 ZKP Transcript:" ))
67
+ control_panel .addWidget (self .transcript )
68
+
69
+ layout .addLayout (control_panel , 30 )
63
70
self .setLayout (layout )
64
71
65
- self .nonce = None
66
- self .commitment = None
72
+ def draw_graph (self ):
73
+ self .scene .clear ()
74
+ self .pos = nx .spring_layout (self .graph , seed = 42 )
75
+ self .node_items = {}
76
+
77
+ for node in self .graph .nodes :
78
+ x , y = self .pos [node ]
79
+ x , y = x * 300 + 300 , y * 300 + 200
80
+ ellipse = QGraphicsEllipseItem (QRectF (x , y , 40 , 40 ))
81
+ ellipse .setBrush (QBrush (Qt .lightGray ))
82
+ ellipse .setPen (QPen (Qt .black ))
83
+ ellipse .setFlag (QGraphicsEllipseItem .ItemIsSelectable )
84
+ ellipse .mousePressEvent = lambda event , n = node : self .set_node_color (n )
85
+ self .scene .addItem (ellipse )
86
+
87
+ label = QGraphicsTextItem (str (node ))
88
+ label .setPos (x + 12 , y + 10 )
89
+ self .scene .addItem (label )
90
+
91
+ self .node_items [node ] = (ellipse , label )
92
+
93
+ for u , v in self .graph .edges :
94
+ x1 , y1 = self .pos [u ]
95
+ x2 , y2 = self .pos [v ]
96
+ x1 , y1 = x1 * 300 + 320 , y1 * 300 + 220
97
+ x2 , y2 = x2 * 300 + 320 , y2 * 300 + 220
98
+ self .scene .addLine (x1 , y1 , x2 , y2 , QPen (Qt .black ))
99
+
100
+ def set_node_color (self , node ):
101
+ color = self .color_select .currentText ()
102
+ self .colors [node ] = color
103
+ ellipse , _ = self .node_items [node ]
104
+ ellipse .setBrush (QBrush (QColor (color )))
105
+ self .status .setText (f"🎨 Node { node } set to { color } (only you know this)" )
106
+ self .transcript .append (f"[Prover] Sets node { node } to { color } " )
107
+
108
+ def commit_colors (self ):
109
+ self .commitments .clear ()
110
+ self .nonces .clear ()
111
+ self .revealed .clear ()
112
+
113
+ missing = [n for n in self .graph .nodes if n not in self .colors ]
114
+ if missing :
115
+ self .status .setText (f"⚠️ Color all nodes first! Missing: { missing } " )
116
+ return
117
+
118
+ for node , color in self .colors .items ():
119
+ nonce = secrets .token_hex (8 )
120
+ self .nonces [node ] = nonce
121
+ self .commitments [node ] = hashlib .sha256 ((color + nonce ).encode ()).hexdigest ()
122
+ ellipse , _ = self .node_items [node ]
123
+ ellipse .setBrush (QBrush (Qt .darkGray ))
124
+ self .transcript .append (f"[Prover] Commits to node { node } with hash = { self .commitments [node ]} " )
125
+
126
+ self .status .setText ("🔒 All node colors committed! Verifier may now challenge an edge." )
67
127
68
- def generate_commitment (self ):
69
- secret = self .secret_input .text ().strip ()
70
- if not secret :
71
- QMessageBox .warning (self , "Input Error" , "Please enter a secret value." )
128
+ def challenge_edge (self ):
129
+ edge = random .choice (list (self .graph .edges ))
130
+ u , v = edge
131
+
132
+ if u in self .revealed or v in self .revealed :
133
+ self .status .setText ("⏭️ This edge was already revealed. Try again." )
72
134
return
73
135
74
- self .nonce = secrets .token_hex (8 )
75
- combined = secret + self .nonce
76
- self .commitment = hashlib .sha256 (combined .encode ()).hexdigest ()
77
-
78
- self .commitment_label .setText (f"Commitment: { self .commitment } " )
79
- self .transcript_box .clear ()
80
- self .transcript_box .append ("Prover commits to a secret using SHA-256(secret || nonce)" )
81
- self .transcript_box .append (f"nonce: { self .nonce } " )
82
- self .transcript_box .append ("Commitment sent to Verifier" )
83
- self .result_label .setText ("✅ Secret committed. You may now reveal." )
84
- self .result_label .setStyleSheet ("color: #1ABC9C; font-weight: bold; padding: 10px;" )
85
-
86
- def reveal_secret (self ):
87
- if not self .commitment :
88
- QMessageBox .warning (self , "No Commitment" , "Please commit to a value first." )
136
+ self .revealed .update ([u , v ])
137
+ result = self .reveal_and_verify (u , v )
138
+ self .status .setText (result )
139
+
140
+ def reveal_and_verify (self , u , v ):
141
+ color_u , color_v = self .colors [u ], self .colors [v ]
142
+ nonce_u , nonce_v = self .nonces [u ], self .nonces [v ]
143
+
144
+ commit_u = hashlib .sha256 ((color_u + nonce_u ).encode ()).hexdigest ()
145
+ commit_v = hashlib .sha256 ((color_v + nonce_v ).encode ()).hexdigest ()
146
+
147
+ self .transcript .append (f"\n [Verifier] Challenges edge ({ u } , { v } )" )
148
+ self .transcript .append (f"→ Prover reveals: node { u } = { color_u } , nonce = { nonce_u } " )
149
+ self .transcript .append (f"→ Prover reveals: node { v } = { color_v } , nonce = { nonce_v } " )
150
+ self .transcript .append (f"→ Verifier recomputes H({ color_u } ||{ nonce_u } ) = { commit_u } " )
151
+ self .transcript .append (f"→ Verifier recomputes H({ color_v } ||{ nonce_v } ) = { commit_v } " )
152
+
153
+ if commit_u != self .commitments [u ] or commit_v != self .commitments [v ]:
154
+ self .transcript .append ("❌ Commitment mismatch! Binding property violated!" )
155
+ return "🚨 Verification Failed! Commitment mismatch (binding broken)"
156
+
157
+ if color_u == color_v :
158
+ self .transcript .append ("❌ Same color for both nodes! Invalid coloring." )
159
+ return "❌ Verification Failed! Adjacent nodes have same color."
160
+
161
+ ellipse_u , _ = self .node_items [u ]
162
+ ellipse_v , _ = self .node_items [v ]
163
+ ellipse_u .setBrush (QBrush (QColor (color_u )))
164
+ ellipse_v .setBrush (QBrush (QColor (color_v )))
165
+
166
+ self .transcript .append ("✅ Commitment verified. Coloring valid for edge." )
167
+ return f"✅ Edge ({ u } , { v } ) verified! { color_u } ≠ { color_v } "
168
+
169
+ def try_to_cheat (self ):
170
+ if not self .commitments :
171
+ self .status .setText ("⚠️ Commit first before cheating." )
89
172
return
90
173
91
- secret = self .secret_input .text ().strip ()
92
- combined = secret + self .nonce
93
- check = hashlib .sha256 (combined .encode ()).hexdigest ()
94
-
95
- self .transcript_box .append ("\n Prover reveals the secret and nonce..." )
96
- self .transcript_box .append (f"secret: { secret } " )
97
- self .transcript_box .append (f"recomputed hash: { check } " )
98
-
99
- if check == self .commitment :
100
- self .result_label .setStyleSheet ("color: #1ABC9C; font-weight: bold; padding: 10px;" )
101
- self .result_label .setText ("✅ Reveal successful. Verifier is convinced." )
102
- self .transcript_box .append ("Verifier confirms: ✅ commitment is valid!" )
103
- else :
104
- self .result_label .setStyleSheet ("color: red; font-weight: bold; padding: 10px;" )
105
- self .result_label .setText ("❌ Reveal failed. Commitment mismatch." )
106
- self .transcript_box .append ("Verifier says: ❌ mismatch in commitment!" )
174
+ node = random .choice (list (self .graph .nodes ))
175
+ new_color = random .choice ([c for c in ["Red" , "Green" , "Blue" ] if c != self .colors [node ]])
176
+ self .colors [node ] = new_color
177
+
178
+ self .status .setText (f"😈 Prover changed color of node { node } to { new_color } post-commit. Now try verifying it!" )
179
+ self .transcript .append (f"🚨 [Prover] Illegally changed color of node { node } to { new_color } after committing!" )
180
+
181
+ def show_education_modal (self ):
182
+ modal = QDialog (self )
183
+ modal .setWindowTitle ("🔍 Understanding Commitment Schemes" )
184
+ layout = QVBoxLayout ()
185
+
186
+ explanation = QLabel ("""
187
+ 🔐 Commitment Schemes
188
+ --------------------------
189
+ ✔️ Hiding: The verifier cannot know the secret until you reveal it.
190
+ ✔️ Binding: You cannot change your mind after committing.
191
+
192
+ Example:
193
+ - You commit to a color by hashing it with a random nonce.
194
+ - You lock that value and show the lock to the verifier.
195
+ - Later, you reveal the color + nonce.
196
+ - Verifier checks if the lock matches the key.
197
+
198
+ 🚫 Hash Collisions: Very unlikely two different messages give same hash.
199
+
200
+ That's why commitment = secrecy + honesty.
201
+ """ )
202
+ explanation .setWordWrap (True )
203
+ layout .addWidget (explanation )
204
+
205
+ button_box = QDialogButtonBox (QDialogButtonBox .Ok )
206
+ button_box .accepted .connect (modal .accept )
207
+ layout .addWidget (button_box )
208
+
209
+ modal .setLayout (layout )
210
+ modal .exec_ ()
211
+
212
+
213
+ if __name__ == "__main__" :
214
+ app = QApplication (sys .argv )
215
+ window = CommitmentGame ()
216
+ window .show ()
217
+ sys .exit (app .exec_ ())
0 commit comments