f.strw
daf5ab6549
- PoW checks implemented - Networking implemented, two instances of Memechain can now be connected to each other and share one blockchain. - Minor front-end changes
174 lines
5.7 KiB
Python
174 lines
5.7 KiB
Python
import hashlib
|
|
import json
|
|
import base64
|
|
import os
|
|
import requests
|
|
from datetime import datetime
|
|
from flask import Flask, jsonify, request, render_template, flash, redirect, url_for
|
|
|
|
class Block:
|
|
def __init__(self, index, timestamp, data, previous_hash, nonce=0):
|
|
self.index = index
|
|
self.timestamp = timestamp
|
|
self.data = data
|
|
self.previous_hash = previous_hash
|
|
self.nonce = nonce
|
|
self.hash = self.calculate_hash()
|
|
|
|
def calculate_hash(self):
|
|
data_string = json.dumps(self.data, sort_keys=True)
|
|
hash_data = str(self.index) + self.timestamp + data_string + self.previous_hash + str(self.nonce)
|
|
return hashlib.sha256(hash_data.encode()).hexdigest()
|
|
|
|
def mine_block(self, difficulty):
|
|
target = '0' * difficulty
|
|
while self.hash[:difficulty] != target:
|
|
self.nonce += 1
|
|
self.hash = self.calculate_hash()
|
|
print(f"Mining block {self.index}: Nonce = {self.nonce}, Hash = {self.hash}")
|
|
print(f"Block {self.index} mined successfully!")
|
|
|
|
|
|
class Blockchain:
|
|
def __init__(self):
|
|
self.chain = []
|
|
self.difficulty = 4
|
|
self.load_chain()
|
|
|
|
def create_genesis_block(self):
|
|
return Block(0, "0000-00-00 00:00:00", "Genesis Block", "0")
|
|
|
|
def get_latest_block(self):
|
|
return self.chain[-1]
|
|
|
|
def add_block(self, new_block):
|
|
new_block.previous_hash = self.get_latest_block().hash
|
|
new_block.hash = new_block.calculate_hash()
|
|
self.chain.append(new_block)
|
|
self.save_chain()
|
|
|
|
def load_chain(self):
|
|
if os.path.exists('blockchain.json'):
|
|
with open('blockchain.json', 'r') as file:
|
|
chain_data = json.load(file)
|
|
for block_data in chain_data:
|
|
block = Block(block_data['index'], block_data['timestamp'], block_data['data'], block_data['previous_hash'])
|
|
block.hash = block_data['hash']
|
|
self.chain.append(block)
|
|
else:
|
|
self.chain.append(self.create_genesis_block())
|
|
|
|
def save_chain(self):
|
|
chain_data = []
|
|
for block in self.chain:
|
|
chain_data.append({
|
|
'index': block.index,
|
|
'timestamp': block.timestamp,
|
|
'data': block.data,
|
|
'previous_hash': block.previous_hash,
|
|
'hash': block.hash
|
|
})
|
|
with open('blockchain.json', 'w') as file:
|
|
json.dump(chain_data, file)
|
|
|
|
def is_chain_valid(self):
|
|
for i in range(1, len(self.chain)):
|
|
current_block = self.chain[i]
|
|
previous_block = self.chain[i - 1]
|
|
|
|
if current_block.hash != current_block.calculate_hash():
|
|
return False
|
|
|
|
if current_block.previous_hash != previous_block.hash:
|
|
return False
|
|
|
|
if current_block.hash[:self.difficulty] != '0' * self.difficulty:
|
|
return False
|
|
|
|
return True
|
|
|
|
app = Flask(__name__)
|
|
app.secret_key = 'lesecretkey'
|
|
blockchain = Blockchain()
|
|
CONNECTED_NODES = []
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template('index.html')
|
|
|
|
@app.route('/upload', methods=['POST'])
|
|
def upload_image():
|
|
file = request.files['image']
|
|
image_data = base64.b64encode(file.read()).decode('utf-8')
|
|
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
block = Block(len(blockchain.chain), current_time, image_data, blockchain.get_latest_block().hash)
|
|
block.mine_block(blockchain.difficulty)
|
|
blockchain.add_block(block)
|
|
|
|
if blockchain.is_chain_valid():
|
|
flash('Image uploaded successfully.', 'success')
|
|
else:
|
|
flash('Corrupted blocks detected. Image upload failed.', 'danger')
|
|
blockchain.chain.pop()
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
@app.route('/chain', methods=['GET'])
|
|
def get_chain():
|
|
chain_data = []
|
|
for block in blockchain.chain:
|
|
chain_data.append({
|
|
'index': block.index,
|
|
'timestamp': block.timestamp,
|
|
'data': block.data,
|
|
'previous_hash': block.previous_hash,
|
|
'hash': block.hash
|
|
})
|
|
return jsonify(chain_data)
|
|
|
|
@app.route('/register_node', methods=['POST'])
|
|
def register_node():
|
|
node_url = request.form['node_url']
|
|
if not node_url.startswith('http://') and not node_url.startswith('https://'):
|
|
node_url = 'http://' + node_url
|
|
if node_url not in CONNECTED_NODES:
|
|
CONNECTED_NODES.append(node_url)
|
|
return redirect(url_for('index'))
|
|
|
|
@app.route('/sync_chain', methods=['GET'])
|
|
def sync_chain():
|
|
longest_chain = None
|
|
current_len = len(blockchain.chain)
|
|
|
|
for node_url in CONNECTED_NODES:
|
|
response = requests.get(f'{node_url}/chain')
|
|
if response.status_code == 200:
|
|
chain_data = response.json()
|
|
chain_len = len(chain_data)
|
|
if chain_len > current_len:
|
|
current_len = chain_len
|
|
longest_chain = chain_data
|
|
|
|
if longest_chain:
|
|
blockchain.chain = [Block(block['index'], block['timestamp'], block['data'], block['previous_hash']) for block in longest_chain]
|
|
blockchain.save_chain()
|
|
flash('Blockchain synchronized successfully', 'success')
|
|
else:
|
|
flash('Blockchain is already up to date', 'info')
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
@app.route('/validate', methods=['GET'])
|
|
def validate_blockchain():
|
|
is_valid = blockchain.is_chain_valid()
|
|
if is_valid:
|
|
return jsonify({"message": "Blockchain is valid"})
|
|
else:
|
|
return jsonify({"message": "Blockchain is invalid"}), 400
|
|
|
|
@app.route('/nodes', methods=['GET'])
|
|
def get_nodes():
|
|
return jsonify(CONNECTED_NODES)
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=5000, debug=True) |