Skip to content

Graph Snapshot Feature: Store & Retrieve Graph History #600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 61 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
e8334df
Introduce a snapshot system that automatically saves graph states usi…
Susmita-Chakrabarty Mar 6, 2025
ce5ca83
Raises ValueError if the requested timestamp is not in the list
Susmita-Chakrabarty Mar 6, 2025
cc4e1d9
Attach snapshot management methods to Graph dynamically
Susmita-Chakrabarty Mar 6, 2025
4f0c5bf
Add timestamped_graph.md describing the use case of timestamped and f…
Susmita-Chakrabarty Mar 6, 2025
e9ec4a2
Fix the formating thing in the timestamped_graph.md
Susmita-Chakrabarty Mar 6, 2025
f52b756
Fix snapshot functionality by adjusting execution order
Susmita-Chakrabarty Mar 7, 2025
ee12f10
Add new file called test_graph_snapshots.py
Susmita-Chakrabarty Mar 7, 2025
760ea13
Restoring all untracked test files
Susmita-Chakrabarty Mar 7, 2025
b8330b2
Fix unreachable code issue by restructuring method execution order
Susmita-Chakrabarty Mar 7, 2025
2207194
Modify test_graph_snapshots.py
Susmita-Chakrabarty Mar 7, 2025
e844a21
Ensures that A and B are actual nodes with a .name attribute
Susmita-Chakrabarty Mar 7, 2025
6700871
Fix snapshot storage and retrieval in Graph class
Susmita-Chakrabarty Mar 7, 2025
691ff5a
Fixed trailing whitespace issues
Susmita-Chakrabarty Mar 7, 2025
e0cc572
Trigger CI Debugging
Susmita-Chakrabarty Mar 7, 2025
165193f
Add function for key rotation
Susmita-Chakrabarty Mar 8, 2025
8155b0d
Write function for generating the secret key
Susmita-Chakrabarty Mar 8, 2025
99fd732
Generate hmac signature
Susmita-Chakrabarty Mar 8, 2025
6e54936
Handle empty graphs in serialization to prevent HMAC errors
Susmita-Chakrabarty Mar 8, 2025
ef7e291
Fix add_snapshot: include HMAC for secure graph state storage
Susmita-Chakrabarty Mar 8, 2025
a1d6c80
Retrieve snapshot and stored HMAC signature
Susmita-Chakrabarty Mar 8, 2025
9eff90b
Serialize the snapshot for HMAC verification
Susmita-Chakrabarty Mar 8, 2025
403c936
Prevent undetected modifications via HMAC integrity checks
Susmita-Chakrabarty Mar 8, 2025
b5bf570
Ensure persistant HMAC keys, proper snapshot handling, and security
Susmita-Chakrabarty Mar 8, 2025
3446ced
Ensure compliance with code quality checks (removed trailing spaces, …
Susmita-Chakrabarty Mar 8, 2025
d407174
Merge branch 'main' into feature/graph-time-series
Susmita331 Mar 8, 2025
75e7b2f
Modify the timestamped_graph.md
Susmita-Chakrabarty Mar 8, 2025
47b0997
Add pedersen_commitment function and docstring explaining parameters
Susmita-Chakrabarty Mar 30, 2025
12017df
Check validate condition of parameters
Susmita-Chakrabarty Mar 30, 2025
abe87b0
Serialize the graph and hash
Susmita-Chakrabarty Mar 30, 2025
6de940d
Generates a secure random blinding factor r ∈ Z_q and computes commit…
Susmita-Chakrabarty Mar 30, 2025
1135d79
Add a toy example in the docstring of pederson algorithm
Susmita-Chakrabarty Mar 30, 2025
e4bcd77
Add flexible serialize_graph() with include_weights toggle
Susmita-Chakrabarty Mar 30, 2025
214d338
Remove doctest example from pedersen_commitment to fix test failure
Susmita-Chakrabarty Mar 30, 2025
6050e1a
Merge branch 'main' into feature/graph-time-series
Susmita331 Mar 30, 2025
8eb8869
Add zkp_gui-game.py to simulate the zero knowledge proof in real time
Susmita-Chakrabarty Apr 14, 2025
2cf926c
Define hardcoded positions for nodes and draw circles with labels
Susmita-Chakrabarty Apr 14, 2025
35b8c64
Initialize the main GUI layout: title, canvas, dropdowns, button, and…
Susmita-Chakrabarty Apr 14, 2025
0153b77
Output each stage: selection, commitment, challenge, and verification
Susmita-Chakrabarty Apr 14, 2025
f94a1c6
delete ZKP_Demo_Tool/zkp_gui_game.py
Susmita-Chakrabarty Apr 14, 2025
27d1d2f
Add zkp_gui_launcher.py
Susmita-Chakrabarty Apr 14, 2025
40c02eb
Add initial ZKP GUI launcher using PyQt5 with styled buttons and scre…
Susmita-Chakrabarty Apr 14, 2025
cc10a55
Add commitment_game.py
Susmita-Chakrabarty Apr 14, 2025
dd70c08
write commitment_game gui screen logic
Susmita-Chakrabarty Apr 14, 2025
3c8e311
rewrite commitment_game.py with full ZKP simulation, prover-verifier …
Susmita-Chakrabarty Apr 14, 2025
d37b4f9
Modify the commit_game
Susmita-Chakrabarty Apr 14, 2025
ec7981a
Add level_selector.py to simulate a more rigourous interactive game
Susmita-Chakrabarty Apr 14, 2025
9ddbac4
Breaking down the commitment_game.py into modular code structure
Susmita-Chakrabarty Apr 14, 2025
61b9034
Add base_level.py
Susmita-Chakrabarty Apr 14, 2025
58914a2
Making the commit_ment game structure scalable
Susmita-Chakrabarty Apr 14, 2025
d3c5669
Implement realistic ZKP mode with one challenge per round and guided …
Susmita-Chakrabarty Apr 14, 2025
9650fee
Add 4 predefined colour for the user
Susmita-Chakrabarty Apr 14, 2025
8ded9fd
Modify level2_ring.py
Susmita-Chakrabarty Apr 15, 2025
1556f16
testing commit
Susmita-Chakrabarty Apr 15, 2025
8dec9c3
Change the ui to style the game
Susmita-Chakrabarty Apr 15, 2025
10ed4f0
Make a demo application of real-world ZKP application
Susmita-Chakrabarty Apr 15, 2025
11b73e8
Make tutorial directory and .py for visual presentation of zkp
Susmita-Chakrabarty Apr 15, 2025
63f8e5e
Add scene2_commitment.py and its logic to show zkp through real life …
Susmita-Chakrabarty Apr 22, 2025
cb0f687
Simulate zkp through bipartate graph
Susmita-Chakrabarty Apr 22, 2025
64d2e6e
Refactor: removed obsolete game code, updated ZKP tutorial scenes
Susmita-Chakrabarty Apr 22, 2025
54146ac
Refactor: removed obsolete game code, updated ZKP tutorial scenes
Susmita-Chakrabarty Apr 22, 2025
c1b0c33
AddReadme.d ensuring project structure using readable tree format wi…
Susmita-Chakrabarty Apr 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added ZKP_Demo_Tool/#
Empty file.
68 changes: 68 additions & 0 deletions ZKP_Demo_Tool/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 🔐 Zero-Knowledge Proof Interactive Tutorial (Graph-Based Simulation)

This project is a **visual and interactive educational tool** that demonstrates the fundamental principles of **Zero-Knowledge Proofs (ZKPs)** using graph-based simulations and commitment schemes. It is built entirely using **Python + PyQt5**, and is designed to engage both cryptography students and mentors with real-world analogies and intuitive illustrations.

Developed by **Susmita Chakrabarty** as part of a GSoC proposal under the PyDataStructs organization.

---

## What It Teaches

This tutorial explains:

- **Commitment Schemes**: Using SHA-256 to create secure, hidden commitments.
- 🔐 **Binding**: Commitments cannot be altered after submission.
- **Hiding**: Commitments reveal no information until the reveal phase.
- **ZKP Challenge Rounds**: Verifier issues a challenge, prover reveals commitment.
- **Graph-Based Real-World Use Cases**: Transaction roles in banking systems, fraud detection, and more.

Each scene is part of a **narrative and interactive journey**, gradually introducing cryptographic guarantees in a way that is fun, memorable, and technically sound.

---

## 📁 Project Structure
```plaintext
ZKP_Demo_Tool/
├── assets/
│ └── scene1.png #Optional illustration or background image
├── tutorial/
│ ├── tutorial_scene.py #Scene 1 - Intro to commitment schemes
│ ├── scene2_commitment.py # Scene 2 - ZKP using transaction graph
│ ├── scene3_bipartate.py # Scene 3 - Bipartite graph simulation
│ └── run_all_scenes.py # Launcher GUI for all scenes
└── README.md # Project documentation

```
## How to Run

### 1. Recommended: Start with the Launcher

```bash
cd /ZKP_DEMO_TOOL/tutorial
python run_all_scenes.py
```
## Designed For

- Cryptography students and researchers
- Mentors reviewing GSoC/academic projects
- Visual learners who prefer intuitive simulations
- Anyone curious about ZKPs in real-world graphs

---

## Future Directions

- Add auto-mode for repeated ZKP rounds with statistics
- Export logs to PDF for classroom use
- Sound or animation effects for fraud detection
- Homomorphic commitments or multi-party extensions

---

## Acknowledgments

This demo was designed to be engaging and intellectually rich while remaining accessible.
Thanks to [Wikipedia - Commitment Scheme](https://en.wikipedia.org/wiki/Commitment_scheme), educational ZKP materials, and feedback from mentors and peers.

Binary file added ZKP_Demo_Tool/assets/scene1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 52 additions & 0 deletions ZKP_Demo_Tool/tutorial/run_all_scenes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import sys
import subprocess
from PyQt5.QtWidgets import (
QApplication, QWidget, QPushButton, QLabel, QVBoxLayout
)
from PyQt5.QtGui import QFont
from PyQt5.QtCore import Qt

class ZKPSceneLauncher(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("🔐 Zero-Knowledge Proof Demo Launcher")
self.setGeometry(300, 200, 500, 320)
self.setStyleSheet("background-color: #1e1e1e; color: white;")

layout = QVBoxLayout()
layout.setSpacing(15)

title = QLabel("🎓 Launch a Scene")
title.setFont(QFont("Arial", 18, QFont.Bold))
title.setAlignment(Qt.AlignCenter)
layout.addWidget(title)

# Scene 1
scene1_btn = QPushButton(" Scene 1: Introduction to Commitment")
scene1_btn.clicked.connect(lambda: self.launch("tutorial_scene.py"))
layout.addWidget(scene1_btn)

# Scene 2
scene2_btn = QPushButton("🎮 Scene 2: Commitment Game")
scene2_btn.clicked.connect(lambda: self.launch("scene2_commitment.py"))
layout.addWidget(scene2_btn)

# Scene 3
scene3_btn = QPushButton("🔀 Scene 3: Bipartite Graph ZKP")
scene3_btn.clicked.connect(lambda: self.launch("scene3_bipartate.py"))
layout.addWidget(scene3_btn)

# Style all buttons
for btn in [scene1_btn, scene2_btn, scene3_btn]:
btn.setStyleSheet("padding: 12px; font-size: 14px; background-color: #2a2a2a; border-radius: 8px;")

self.setLayout(layout)

def launch(self, script_path):
subprocess.Popen(["python", script_path])

if __name__ == "__main__":
app = QApplication(sys.argv)
launcher = ZKPSceneLauncher()
launcher.show()
sys.exit(app.exec_())
193 changes: 193 additions & 0 deletions ZKP_Demo_Tool/tutorial/scene2_commitment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import sys
import subprocess
import hashlib
import random
from PyQt5.QtWidgets import (
QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QGraphicsScene,
QGraphicsView, QGraphicsEllipseItem, QGraphicsTextItem, QGraphicsLineItem,
QHBoxLayout, QScrollArea
)
from PyQt5.QtGui import QFont, QPen, QBrush, QColor
from PyQt5.QtCore import Qt, QPointF, QLineF


class ZKPNode:
def __init__(self, name, role_label, position, color):
self.name = name
self.role_label = role_label
self.position = position
self.color = color
self.nonce = str(random.randint(10000, 99999))
self.commitment = hashlib.sha256((role_label + self.nonce).encode()).hexdigest()
self.revealed_role = role_label
self.revealed_nonce = self.nonce


class NarrationEngine:
@staticmethod
def format_log(node1, node2, binding_ok, hiding_ok, binding_broken=False):
log = f"""
🎥 Security Camera Activated: Monitoring link → {node1.name} ↔ {node2.name}

🔐 Commitments Submitted:
• {node1.name} → SHA256(????) = {node1.commitment[:12]}...
• {node2.name} → SHA256(????) = {node2.commitment[:12]}...

Challenge Issued → Reveal phase begins...

🔓 Revealed:
• {node1.name} → \"{node1.revealed_role}\" with nonce = {node1.revealed_nonce}
• {node2.name} → \"{node2.revealed_role}\" with nonce = {node2.revealed_nonce}

Recomputed Hashes:
"""
if binding_broken:
log += " ❌ Mismatch detected — Binding broken!\n Prover tried to change their role after committing.\n\n"
else:
log += " ✅ Matches original commitments — Binding held.\n\n"

if not binding_broken and hiding_ok:
log += "✅ Hiding held — Verifier only learns that roles are different.\nZKP passed successfully.\n"
elif not binding_broken and not hiding_ok:
log += f"⚠️ Hiding broken — {node1.name} and {node2.name} revealed same role!\nZKP failed. Verifier now knows part of the secret mapping.\n"

log += """
📘 Explanation:
- Binding ensures that once a role is committed with a hash, it can't be changed.
- Hiding ensures that the hash doesn't reveal the actual role until the reveal phase.
- If two adjacent nodes share the same role, it can indicate a conflict of interest or security flaw.
For example, if an ATM and a Validator are the same role, one could validate its own transaction.
This breaks the fundamental principle of role separation in secure systems.
"""
return log


class SceneZKPGraph(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("🔐 ZKP Graph Simulation - Security Log Mode")
self.setGeometry(150, 100, 1100, 750)
self.setStyleSheet("background-color: #121212; color: white;")

self.scene = QGraphicsScene()
self.view = QGraphicsView(self.scene)
self.view.setStyleSheet("background-color: #1e1e1e; border: none;")

# Scrollable narration box
self.text_output = QLabel()
self.text_output.setWordWrap(True)
self.text_output.setFont(QFont("Courier New", 12))
self.text_output.setStyleSheet("background-color: #1c1c1c; padding: 10px; border: 1px solid #444; color: white;")
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setWidget(self.text_output)
scroll.setMinimumHeight(250)

self.verify_button = QPushButton("🎯 Simulate ZKP Verification")
self.verify_button.setFont(QFont("Arial", 14))
self.verify_button.setStyleSheet("padding: 10px; background-color: #2d3436; color: white; border-radius: 8px;")
self.verify_button.clicked.connect(self.reveal_connection)

self.next_button = QPushButton("➡ Next: Scene 3 - Bipartate Graph")
self.next_button.setFont(QFont("Arial", 13, QFont.Bold))
self.next_button.setStyleSheet(
"background-color: #0055ff; color: white; padding: 10px; border-radius: 10px;"
)
self.next_button.clicked.connect(self.go_to_next_scene)

layout = QVBoxLayout()
layout.addWidget(self.view)
layout.addWidget(self.verify_button)
layout.addWidget(scroll)

# Center the button using a horizontal layout
button_layout = QHBoxLayout()
button_layout.addStretch()
button_layout.addWidget(self.next_button)
button_layout.addStretch()
layout.addLayout(button_layout)

self.setLayout(layout)

self.nodes = []
self.node_graphics = {}
self.edges = []

self.build_graph()

def build_graph(self):
layout = [
("ATM", "Initiator", QPointF(150, 150), QColor("#e74c3c")),
("Validator", "Validator", QPointF(500, 150), QColor("#f1c40f")),
("Merchant", "Receiver", QPointF(300, 350), QColor("#2ecc71")),
("Customer", "Initiator", QPointF(850, 150), QColor("#e74c3c")),
("Treasury", "Receiver", QPointF(650, 400), QColor("#2ecc71")),
("Insider", "Validator", QPointF(150, 450), QColor("#f1c40f"))
]

connections = [(0, 1), (1, 2), (2, 4), (3, 1), (5, 2), (0, 5)]

for name, role, pos, color in layout:
node = ZKPNode(name, role, pos, color)
self.nodes.append(node)

for idx, node in enumerate(self.nodes):
ellipse = QGraphicsEllipseItem(-30, -30, 60, 60)
ellipse.setBrush(QBrush(Qt.gray))
ellipse.setPen(QPen(Qt.white, 2))
ellipse.setPos(node.position)
self.scene.addItem(ellipse)

lock = QGraphicsTextItem("🔐")
lock.setFont(QFont("Arial", 16))
lock.setDefaultTextColor(Qt.white)
lock.setPos(node.position.x() - 10, node.position.y() - 55)
self.scene.addItem(lock)

label = QGraphicsTextItem(node.name)
label.setFont(QFont("Arial", 12))
label.setDefaultTextColor(Qt.white)
label.setPos(node.position.x() - 30, node.position.y() + 35)
self.scene.addItem(label)

self.node_graphics[idx] = (ellipse, lock, label)

for a, b in connections:
p1 = self.nodes[a].position
p2 = self.nodes[b].position
line = QGraphicsLineItem(QLineF(p1, p2))
line.setPen(QPen(Qt.white, 2))
self.scene.addItem(line)
self.edges.append((a, b))

def reveal_connection(self):
a, b = random.choice(self.edges)
node1, node2 = self.nodes[a], self.nodes[b]

self.node_graphics[a][0].setBrush(QBrush(node1.color))
self.node_graphics[b][0].setBrush(QBrush(node2.color))

recomputed_hash_1 = hashlib.sha256((node1.revealed_role + node1.revealed_nonce).encode()).hexdigest()
recomputed_hash_2 = hashlib.sha256((node2.revealed_role + node2.revealed_nonce).encode()).hexdigest()

binding_ok = (recomputed_hash_1 == node1.commitment and recomputed_hash_2 == node2.commitment)
hiding_ok = node1.revealed_role != node2.revealed_role

log = NarrationEngine.format_log(
node1, node2,
binding_ok=binding_ok,
hiding_ok=hiding_ok,
binding_broken=not binding_ok
)
self.text_output.setText(log)

def go_to_next_scene(self):
subprocess.Popen(["python", "scene3_bipartate.py"])
self.close()


if __name__ == "__main__":
app = QApplication(sys.argv)
window = SceneZKPGraph()
window.show()
sys.exit(app.exec_())
Loading
Loading