v. 1
This commit is contained in:
commit
d8e8636705
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
venv/
|
||||
blockchain.json
|
158
blockchain.py
Normal file
158
blockchain.py
Normal file
|
@ -0,0 +1,158 @@
|
|||
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):
|
||||
self.index = index
|
||||
self.timestamp = timestamp
|
||||
self.data = data
|
||||
self.previous_hash = previous_hash
|
||||
self.hash = self.calculate_hash()
|
||||
|
||||
def calculate_hash(self):
|
||||
data_string = json.dumps(self.data, sort_keys=True)
|
||||
return hashlib.sha256((str(self.index) + self.timestamp + data_string + self.previous_hash).encode()).hexdigest()
|
||||
|
||||
class Blockchain:
|
||||
def __init__(self):
|
||||
self.chain = []
|
||||
self.load_chain()
|
||||
|
||||
def create_genesis_block(self):
|
||||
return Block(0, "00-00-0000", "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
|
||||
|
||||
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)
|
||||
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)
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
requests
|
||||
flask
|
81
templates/index.html
Normal file
81
templates/index.html
Normal file
|
@ -0,0 +1,81 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Memechain</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Memechain</h1>
|
||||
<em>your glorified nfts or something</em>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div style="margin-top: 16px;" class="alert alert-{{ category }}" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="image">Select Image:</label>
|
||||
<input type="file" class="form-control-file" id="image" name="image" accept="image/*" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Upload</button>
|
||||
</form>
|
||||
<hr>
|
||||
<h2>Connected Nodes</h2>
|
||||
<form action="/register_node" method="post">
|
||||
<div class="form-group">
|
||||
<label for="node_url">Node URL:</label>
|
||||
<input type="text" class="form-control" id="node_url" name="node_url" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Connect Node</button>
|
||||
</form>
|
||||
<ul id="node-list" class="list-group mt-3"></ul>
|
||||
<hr>
|
||||
<a href="/sync_chain" class="btn btn-primary">Sync Blockchain</a>
|
||||
<hr>
|
||||
<h2>Blockchain</h2>
|
||||
<div id="blockchain"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
<script>
|
||||
fetch('/chain')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const blockchainDiv = document.getElementById('blockchain');
|
||||
data.forEach(block => {
|
||||
const blockDiv = document.createElement('div');
|
||||
blockDiv.innerHTML = `
|
||||
<p>Index: ${block.index}</p>
|
||||
<p>Timestamp: ${block.timestamp}</p>
|
||||
<p>Image: <img style="height: 50%; width: 50%;" src="data:image/png;base64,${block.data}" alt="Block Image"></p>
|
||||
<p>Previous Hash: ${block.previous_hash}</p>
|
||||
<p>Hash: ${block.hash}</p>
|
||||
<hr>
|
||||
`;
|
||||
blockchainDiv.appendChild(blockDiv);
|
||||
});
|
||||
});
|
||||
|
||||
fetch('/nodes')
|
||||
.then(response => response.json())
|
||||
.then(nodes => {
|
||||
const nodeList = document.getElementById('node-list');
|
||||
nodes.forEach(node => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.textContent = node;
|
||||
listItem.classList.add('list-group-item');
|
||||
nodeList.appendChild(listItem);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue