-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Blockchain Wiki
Link: https://github.com/theurn15/blockchain
The Blockchain component in this repository provides a simplified, educational implementation of a blockchain ledger used to record encrypted model‐weight transactions during federated learning. It is not intended for production use—rather, it demonstrates how model updates can be securely packaged, transmitted, and aggregated in a transparent, tamper‐evident manner. This repository is inspired by the framework presented in:
Alshareet, O., Awasthi, A. “A Novel Framework for Integrating Blockchain-Driven Federated Learning with Neural Networks in E‑Commerce.” Journal of Network and Systems Management 33, 56 (2025). https://doi.org/10.1007/s10922-025-09928-x
Key features:
- Block structure: Each block contains an index, timestamp, proof (nonce), previous block’s hash, and a list of transactions.
- Proof‐of‐Work: A very basic "mining" mechanism that requires finding a nonce such that the SHA‐256 hash of last_proof||proof begins with four zeros ("0000").
- Encrypted transactions: Model weights (logistic regression coefficients and intercepts) are serialized to JSON, encrypted with a symmetric key (Fernet), and stored as transaction data.
- Chain validation: A single valid_proof method ensures the PoW condition. No full chain‐validation routine is provided (this demo assumes all miners behave honestly).
All blockchain code resides in: blockchain.py
link: https://github.com/theurn15/blockchain
The Blockchain class lives alongside federated‐learning logic (in the same Python script) to demonstrate how each client’s local model weights get recorded and how the server adds an aggregated block.
class Blockchain: def init(self): self.chain = [] self.current_transactions = [] # Create the "genesis" block self.create_block(proof=1, previous_hash='0')
def create_block(self, proof, previous_hash):
"""
Create a new block in the chain.
- proof: The proof (nonce) obtained by the proof‐of‐work algorithm.
- previous_hash: SHA‐256 hash of the previous block.
Returns the newly created block (as a dict).
"""
block = {
'index': len(self.chain) + 1,
'timestamp': time.time(),
'proof': proof,
'previous_hash': previous_hash,
'transactions': self.current_transactions,
}
# Reset transaction list for the next block
self.current_transactions = []
self.chain.append(block)
return block
def add_encrypted_transaction(self, sender, receiver, encrypted_data):
"""
Add a new encrypted transaction to the current list.
- sender: String identifier (e.g., "Client0" or "Server")
- receiver: String identifier (e.g., "Server" or "Clients")
- encrypted_data: Bytes object (Fernet‐encrypted JSON payload)
"""
self.current_transactions.append({
'sender': sender,
'receiver': receiver,
'encrypted_data': encrypted_data,
})
def proof_of_work(self, last_proof):
"""
Simple PoW algorithm:
Continuously increment a nonce until hash(last_proof || proof) begins with "0000".
Returns the found proof (integer).
"""
proof = 0
while not self.valid_proof(last_proof, proof):
proof += 1
return proof
@staticmethod
def valid_proof(last_proof, proof):
"""
Check whether hash(last_proof || proof) (SHA‐256) has the required format.
Returns True if the first four characters of the hexdigest are "0000".
"""
guess = f'{last_proof}{proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4] == "0000"
@staticmethod
def hash(block):
"""
Create a SHA‐256 hash of a block dictionary (converted to string bytes).
This is used to link blocks in the chain.
"""
block_string = str(block).encode()
return hashlib.sha256(block_string).hexdigest()
@property
def last_block(self):
"""Return the most recently added block in the chain."""
return self.chain[-1]
-
Initialization (Genesis Block) blockchain = Blockchain() encryption_key = Fernet.generate_key()
- Creates the "genesis" block with proof = 1 and previous_hash = "0".
- Generates a symmetric key (encryption_key) for encrypting all transactions in this run.
-
Clients Add Local Model Transactions After each client trains a local LogisticRegression model, its weight parameters are packaged and encrypted: model_weights = { 'coef_': client_models[i].coef_.tolist(), 'intercept_': client_models[i].intercept_.tolist() } fernet = Fernet(encryption_key) encrypted_model_weights = fernet.encrypt(json.dumps(model_weights).encode()) blockchain.add_encrypted_transaction(f'Client{i}', 'Server', encrypted_model_weights)
- Clients call add_encrypted_transaction(sender, receiver, encrypted_data) to queue up their model‐weight blob for inclusion in the next block.
-
Viewing Encrypted Transactions (Console Output) Immediately after collecting a round of client transactions, the demo prints each transaction’s sender, receiver, and a truncated base‐64 string of the encrypted data: for tx in blockchain.current_transactions[-num_clients:]: sender = tx['sender'] receiver = tx['receiver'] encrypted_data_str = tx['encrypted_data'].decode('utf-8') truncated_data = encrypted_data_str[:50] + '...' if len(encrypted_data_str) > 50 else encrypted_data_str print(f"Sender: {sender}, Receiver: {receiver}, Encrypted Data: {truncated_data}")
- This is purely for demonstration; in a real deployment you would not print raw encrypted payloads.
-
Server Aggregates Models & Mines a New Block Once all client models are received, the server does the following:
avg_coef = np.mean([clf.coef_ for clf in client_models], axis=0) avg_intercept = np.mean([clf.intercept_ for clf in client_models], axis=0) federated_model = LogisticRegression() federated_model.fit(X[0], adjusted_labels[0]) # Dummy fit to initialize attributes federated_model.coef_ = avg_coef federated_model.intercept_ = avg_intercept
last_block = blockchain.last_block last_proof = last_block['proof'] proof = blockchain.proof_of_work(last_proof)
fernet = Fernet(encryption_key) encrypted_federated_model = fernet.encrypt(str({ 'coef_': federated_model.coef_.tolist(), 'intercept_': federated_model.intercept_.tolist(), }).encode()) blockchain.add_encrypted_transaction('Server', 'Clients', encrypted_federated_model)
blockchain.create_block(proof, blockchain.hash(last_block))
- The server’s block includes one transaction: the encrypted federated (aggregated) model.
- The new block’s proof is found by repeatedly hashing last_proof||proof until the first four hex characters are "0000".
- Once the block is appended, current_transactions is cleared for the next round.
-
Run the Script python blockchain.py As the script executes, you will see console output for:
- Each client’s encrypted model‐weight transaction (truncated).
- The server’s aggregated (encrypted) model transaction.
- Complaint‐handling messages (unrelated to the blockchain).
- Complete block details and final metrics DataFrame.
-
Inspecting the Chain At the end of blockchain.py, the demo prints every block’s content, including:
- index (block height)
- timestamp (Unix epoch)
- proof (nonce found via PoW)
- previous_hash (link to parent)
- transactions (sender, receiver, first 50 bytes of encrypted data) This helps visualize how each block links to the previous one and what payload it carries.
-
Encrypt/Decrypt
- The same encryption_key (generated at runtime) is used to encrypt all client and server transactions.
- If you want to decrypt a transaction for inspection: fernet = Fernet(encryption_key) decrypted_bytes = fernet.decrypt(blockchain.chain[<block_index>]['transactions'][<tx_index>]['encrypted_data']) model_dict = json.loads(decrypted_bytes.decode('utf-8'))
- This code snippet is not provided in blockchain.py itself but can be added if you wish to verify the payload.
-
Modifying Proof‐of‐Work Difficulty
- Currently, valid_proof requires the hash to start with "0000".
- To lower difficulty, reduce the number of leading zeros checked (e.g. "00").
- To increase difficulty, check more leading zeros (e.g. "00000").
-
Extending to a Real Blockchain This educational implementation omits many features of a robust production blockchain:
- Consensus protocol (e.g., no “longest chain” rule, no network of nodes).
- Full chain validation (no method to iterate and verify every block’s proof and previous_hash).
- Fork handling (no branching, no conflict resolution).
- Digital signatures (clients are not signing transactions; we assume trust that they encrypt with a shared key).
To extend this into a more complete system, you could:
- Implement a validate_chain() method that recomputes each block’s hash and valid_proof.
- Add client‐side key management and digital signatures to verify who submitted each transaction.
- Introduce a peer‐to‐peer networking layer (e.g., Flask endpoints for "/mine" and "/transactions/new") so that multiple nodes can share blocks.
Blockchain
- init(): Initializes the chain list and current_transactions, then creates a genesis block.
- create_block(proof, previous_hash): Appends a new block (with index, timestamp, proof, previous_hash, and queued transactions) to the chain.
- add_encrypted_transaction(sender, receiver, encrypted_data): Adds a dictionary with sender, receiver, and encrypted_data to current_transactions.
- proof_of_work(last_proof): Finds a nonce (proof) such that sha256(f"{last_proof}{proof}")[:4] == "0000".
- valid_proof(last_proof, proof): Static method that checks the above condition.
- hash(block): Static method that returns the SHA‐256 hexdigest of a block’s string representation.
- last_block (property): Returns the most recently added block in the chain.
ComplaintHandler
-
Although not strictly part of the blockchain itself, ComplaintHandler is included in the same file. It simulates a simple ISO 10001:2018 complaint workflow: class ComplaintHandler: def init(self): self.complaints = []
def receive_complaint(self, client_name, complaint_text): self.complaints.append({ 'client_name': client_name, 'complaint_text': complaint_text, 'timestamp': time.time(), }) -
Each “complaint” is simply appended to a list.
-
In blockchain.py’s “Step 15,” each client sends one complaint, and the server prints them. This demonstrates how a complaint handling framework might be integrated alongside a federated‐learning blockchain.
Below is a quick snippet (not in the demo) showing how you might programmatically inspect and decrypt a single transaction from the chain:
from cryptography.fernet import Fernet import json
blockchain = Blockchain() encryption_key = Fernet.generate_key() # (In the demo, this key is generated at runtime.)
block_index = 2 tx_index = 0 # first transaction in that block
encrypted_bytes = blockchain.chain[block_index]['transactions'][tx_index]['encrypted_data']
fernet = Fernet(encryption_key) decrypted_bytes = fernet.decrypt(encrypted_bytes) model_dict = json.loads(decrypted_bytes.decode('utf-8'))
print("Decrypted Model Weights:", model_dict)
-
Not a Production Blockchain
- No network communication (all logic runs in a single process).
- No digital signatures or identity management—every node shares the same encryption_key.
- No mechanism to detect or resolve forks; blocks are always appended in order.
-
Simplified Encryption
- Model weights are encrypted as a JSON string.
- In a real‐world scenario, you would likely use public‐key cryptography (e.g., each client has its own keypair) and sign transactions.
-
Limited Proof‐of‐Work
- Difficulty is fixed (“find four leading zeros”).
- No dynamic adjustment, no reward, no full mining incentives.
-
Intended Use
- Educational / Demo only: Illustrates how federated‐learning updates can be chained into blocks and encrypted.
- Citation Required: If you use or adapt any portion of this code, please cite: Alshareet, O., Awasthi, A. “A Novel Framework for Integrating Blockchain-Driven Federated Learning with Neural Networks in E‑Commerce.” Journal of Network and Systems Management 33, 56 (2025). https://doi.org/10.1007/s10922-025-09928-x
- The original paper: Alshareet, O., Awasthi, A. “A Novel Framework for Integrating Blockchain‑Driven Federated Learning with Neural Networks in E‑Commerce.” Journal of Network and Systems Management 33, 56 (2025). https://doi.org/10.1007/s10922‑025‑09928‑X
- Practical blockchain tutorials: - “Build a Simple Blockchain in Python” (various blog posts and GitHub examples). - “Understanding Proof of Work” and how nonce finding ties blocks together.
- Python cryptography package documentation (for Fernet): https://cryptography.io/en/latest/fernet/