-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSCP(Stellar Consensus Protocol)
More file actions
191 lines (163 loc) · 6.55 KB
/
SCP(Stellar Consensus Protocol)
File metadata and controls
191 lines (163 loc) · 6.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#PiNetwork Consensus
import hashlib
import time
import sqlite3
from typing import List, Set
from ecdsa import SigningKey, VerifyingKey, SECP256k1
# Database Setup
def initialize_database():
connection = sqlite3.connect("blockchain_keys.db")
cursor = connection.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS public_keys (
node_id TEXT PRIMARY KEY,
public_key TEXT
)
""")
connection.commit()
connection.close()
def save_public_key(node_id: str, public_key: VerifyingKey):
connection = sqlite3.connect("blockchain_keys.db")
cursor = connection.cursor()
cursor.execute("""
INSERT OR REPLACE INTO public_keys (node_id, public_key)
VALUES (?, ?)
""", (node_id, public_key.to_pem().decode()))
connection.commit()
connection.close()
def get_public_key(node_id: str) -> VerifyingKey:
connection = sqlite3.connect("blockchain_keys.db")
cursor = connection.cursor()
cursor.execute("""
SELECT public_key FROM public_keys WHERE node_id = ?
""", (node_id,))
result = cursor.fetchone()
connection.close()
if result:
return VerifyingKey.from_pem(result[0].encode())
else:
raise ValueError(f"Public key for node {node_id} not found.")
# Blockchain Classes
class Block:
def __init__(self, index: int, timestamp: str, data: str, previous_hash: str):
self.index = index
self.timestamp = timestamp
self.data = data
self.previous_hash = previous_hash
self.hash = self.calculate_hash()
self.signature = None # Signature for block validation
self.proposer_id = None # Node ID of the block proposer
def calculate_hash(self) -> str:
return hashlib.sha256(f"{self.index}{self.timestamp}{self.data}{self.previous_hash}".encode()).hexdigest()
def sign_block(self, private_key: SigningKey, proposer_id: str):
"""
Sign the block with the private key of the proposing node.
"""
if not private_key:
raise ValueError("Private key is required to sign the block.")
self.signature = private_key.sign(self.hash.encode())
self.proposer_id = proposer_id
def verify_signature(self) -> bool:
"""
Verify the block signature using the public key from the database.
"""
if not self.signature or not self.proposer_id:
raise ValueError("Block has no signature or proposer ID.")
public_key = get_public_key(self.proposer_id)
return public_key.verify(self.signature, self.hash.encode())
class Node:
def __init__(self, node_id: str):
self.node_id = node_id
self.private_key = SigningKey.generate(curve=SECP256k1) # Private key for signing
self.public_key = self.private_key.get_verifying_key() # Public key for verification
self.neighbors: Set['Node'] = set()
self.blockchain: List[Block] = [self.create_genesis_block()]
# Save public key to database
save_public_key(self.node_id, self.public_key)
def create_genesis_block(self) -> Block:
genesis_block = Block(0, time.time(), "Genesis Block", "0")
genesis_block.sign_block(self.private_key, self.node_id)
return genesis_block
def get_latest_block(self) -> Block:
return self.blockchain[-1]
def propose_block(self, data: str) -> Block:
new_block = Block(
len(self.blockchain),
time.time(),
data,
self.get_latest_block().hash
)
new_block.sign_block(self.private_key, self.node_id)
return new_block
def validate_block(self, block: Block) -> bool:
"""
Validate block signature and hash integrity.
"""
latest_block = self.get_latest_block()
if block.previous_hash != latest_block.hash:
print(f"Invalid previous hash for block {block.index}.")
return False
if block.hash != block.calculate_hash():
print(f"Invalid hash for block {block.index}.")
return False
if not block.verify_signature():
print(f"Invalid signature for block {block.index}.")
return False
return True
class SCPConsensus:
def __init__(self):
self.network: dict[str, Node] = {}
def register_node(self, node: Node):
self.network[node.node_id] = node
def add_connection(self, node_a: str, node_b: str):
self.network[node_a].neighbors.add(self.network[node_b])
self.network[node_b].neighbors.add(self.network[node_a])
def federated_voting(self, proposed_block: Block, proposing_node: str) -> bool:
"""
Simulates federated voting with signature validation.
"""
proposing_node_obj = self.network[proposing_node]
votes = 0
for neighbor in proposing_node_obj.neighbors:
if neighbor.validate_block(proposed_block):
votes += 1
required_votes = len(proposing_node_obj.neighbors) // 2 + 1
return votes >= required_votes
def propose_and_commit(self, node_id: str, data: str):
proposing_node = self.network[node_id]
proposed_block = proposing_node.propose_block(data)
if self.federated_voting(proposed_block, node_id):
print(f"Block accepted by majority: {proposed_block.hash}")
for node in self.network.values():
node.add_block(proposed_block)
else:
print("Block rejected by consensus.")
# Example Usage
if __name__ == "__main__":
# Initialize database
initialize_database()
# Create consensus system
consensus = SCPConsensus()
# Create nodes
node_a = Node("Node_A")
node_b = Node("Node_B")
node_c = Node("Node_C")
node_d = Node("Node_D")
# Register nodes
consensus.register_node(node_a)
consensus.register_node(node_b)
consensus.register_node(node_c)
consensus.register_node(node_d)
# Establish connections
consensus.add_connection("Node_A", "Node_B")
consensus.add_connection("Node_A", "Node_C")
consensus.add_connection("Node_B", "Node_C")
consensus.add_connection("Node_C", "Node_D")
# Propose and commit blocks
consensus.propose_and_commit("Node_A", "Block 1 Data")
consensus.propose_and_commit("Node_B", "Block 2 Data")
# Print blockchains
for node_id, node in consensus.network.items():
print(f"\nBlockchain for {node_id}:")
for block in node.blockchain:
print(f"Index: {block.index}, Hash: {block.hash}, Data: {block.data}, Signature: {block.signature is not None}")