updates
This commit is contained in:
parent
bfe4de5016
commit
fd7cf5e2f9
|
@ -1,8 +1,10 @@
|
|||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
|
||||
from database import Block, db
|
||||
from database import Block, db, DateTimeEncoder
|
||||
from helpers import json_serializer
|
||||
|
||||
|
||||
logger = logging.getLogger('root.blockchain')
|
||||
|
@ -12,10 +14,14 @@ class Blockchain:
|
|||
def __init__(self):
|
||||
self.current_transactions = []
|
||||
self.difficulty = 4
|
||||
self.current_block = None
|
||||
|
||||
# Create the genesis block if necessary
|
||||
if not self.get_blocks(0):
|
||||
self.new_block(previous_hash='1', proof=100)
|
||||
if not self.last_block:
|
||||
block = self.build_block()
|
||||
block['hash'] = self.hash(block)
|
||||
self.save_block(block)
|
||||
|
||||
logger.info("✨ Created genesis block")
|
||||
|
||||
logger.info("Blockchain Initiated")
|
||||
|
@ -59,31 +65,32 @@ class Blockchain:
|
|||
|
||||
return True
|
||||
|
||||
def new_block(self, proof, previous_hash):
|
||||
def build_block(self):
|
||||
"""
|
||||
Create a new Block in the Blockchain
|
||||
|
||||
:param proof: The proof given by the Proof of Work algorithm
|
||||
:param previous_hash: Hash of previous Block
|
||||
:param height: The Height of the new block
|
||||
:return: New Block
|
||||
"""
|
||||
last_block = self.last_block
|
||||
|
||||
block = Block(
|
||||
timestamp=datetime.now(),
|
||||
transactions=self.current_transactions,
|
||||
proof=proof,
|
||||
previous_hash=previous_hash,
|
||||
)
|
||||
return {
|
||||
'transactions': self.current_transactions,
|
||||
'previous_hash': last_block.hash if last_block else 0,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
def save_block(self, block_dict):
|
||||
"""
|
||||
Saves Block to the database and resets outstanding transactions
|
||||
|
||||
:param block_dict: A dictionary representation of a Block
|
||||
:return:
|
||||
"""
|
||||
block = Block(**block_dict)
|
||||
db.add(block)
|
||||
db.commit()
|
||||
|
||||
# Reset the current list of transactions
|
||||
self.current_transactions = []
|
||||
|
||||
return block
|
||||
|
||||
def new_transaction(self, sender, recipient, amount):
|
||||
"""
|
||||
Creates a new transaction to go into the next mined Block
|
||||
|
@ -113,10 +120,7 @@ class Blockchain:
|
|||
@staticmethod
|
||||
def hash(block):
|
||||
"""
|
||||
Creates a SHA-256 hash of a Block
|
||||
|
||||
:param block: Block
|
||||
Creates a SHA-256 hash of the fields for a Block
|
||||
"""
|
||||
|
||||
block_bytes = block.to_json().encode()
|
||||
return hashlib.sha256(block_bytes).hexdigest()
|
||||
json_string = json.dumps(block, sort_keys=True, cls=DateTimeEncoder)
|
||||
return sha256(json_string.encode()).hexdigest()
|
||||
|
|
23
database.py
23
database.py
|
@ -1,4 +1,6 @@
|
|||
import json
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
|
||||
from sqlalchemy import Column, DateTime, Integer, PickleType, String, create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
|
@ -9,8 +11,6 @@ from sqlalchemy.orm import scoped_session, sessionmaker
|
|||
engine = create_engine('sqlite:///electron.db')
|
||||
db = scoped_session(sessionmaker(bind=engine))
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
class DateTimeEncoder(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
|
@ -19,7 +19,13 @@ class DateTimeEncoder(json.JSONEncoder):
|
|||
|
||||
return json.JSONEncoder.default(self, o)
|
||||
|
||||
class BaseModel(object):
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class BaseModel(Base):
|
||||
__abstract__ = True
|
||||
|
||||
@declared_attr
|
||||
def __tablename__(self):
|
||||
"""
|
||||
|
@ -40,15 +46,13 @@ class BaseModel(object):
|
|||
return json.dumps(self.to_dict(), sort_keys=True, cls=DateTimeEncoder)
|
||||
|
||||
|
||||
Base = declarative_base(cls=BaseModel)
|
||||
|
||||
|
||||
class Peer(Base):
|
||||
class Peer(BaseModel):
|
||||
identifier = Column(String(32), primary_key=True)
|
||||
hostname = Column(String, index=True, unique=True)
|
||||
timestamp = Column(DateTime, index=True)
|
||||
|
||||
|
||||
class Block(Base):
|
||||
class Block(BaseModel):
|
||||
height = Column(Integer, primary_key=True, autoincrement=True)
|
||||
timestamp = Column(DateTime, index=True)
|
||||
transactions = Column(PickleType)
|
||||
|
@ -57,7 +61,7 @@ class Block(Base):
|
|||
hash = Column(String(64))
|
||||
|
||||
|
||||
class Config(Base):
|
||||
class Config(BaseModel):
|
||||
key = Column(String(64), primary_key=True, unique=True)
|
||||
value = Column(PickleType)
|
||||
|
||||
|
@ -68,3 +72,4 @@ def reset_db():
|
|||
"""
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
|
|
12
helpers.py
12
helpers.py
|
@ -1,3 +1,5 @@
|
|||
from datetime import date, datetime
|
||||
|
||||
from sqlalchemy import func
|
||||
|
||||
from database import Config, Peer, db
|
||||
|
@ -35,3 +37,13 @@ def get_random_peers(limit=10):
|
|||
:return:
|
||||
"""
|
||||
return db.query(Peer).order_by(func.random()).limit(limit)
|
||||
|
||||
|
||||
def json_serializer(obj):
|
||||
"""
|
||||
JSON serializer for objects not serializable by default json code
|
||||
"""
|
||||
|
||||
if isinstance(obj, (datetime, date)):
|
||||
return obj.isoformat()
|
||||
raise TypeError("Type %s not serializable" % type(obj))
|
||||
|
|
49
mining.py
49
mining.py
|
@ -1,54 +1,47 @@
|
|||
import logging
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
import signal
|
||||
|
||||
from blockchain import Blockchain
|
||||
|
||||
|
||||
log = logging.getLogger('root.mining')
|
||||
|
||||
|
||||
def valid_proof(last_hash, proof, target):
|
||||
"""
|
||||
Validates the Proof
|
||||
|
||||
:param last_hash: Hash of the previous block
|
||||
:param proof: Current Proof
|
||||
:param target: Target Difficulty
|
||||
:return: True if correct, False if not.
|
||||
"""
|
||||
|
||||
guess = f'{last_hash}{proof}'.encode()
|
||||
guess_hash = sha256(guess).hexdigest()
|
||||
return guess_hash < target
|
||||
|
||||
|
||||
def proof_of_work(last_hash, difficulty, event):
|
||||
def proof_of_work(current_block, difficulty, event):
|
||||
"""
|
||||
Simple Proof of Work Algorithm
|
||||
|
||||
:param last_hash: The hash of the previous block
|
||||
:param current_block: The partially complete block currently being mined
|
||||
:param difficulty: The minimum number of leading zeros
|
||||
"""
|
||||
|
||||
# String of 64 f's replaced with 3 leading zeros (if the difficulty is 3): 000fff...f
|
||||
target = str.ljust("0" * difficulty, 64, "f")
|
||||
|
||||
proof = 0
|
||||
while valid_proof(last_hash, proof, target) is False:
|
||||
current_block['timestamp'] = datetime.now()
|
||||
current_block['proof'] = 0
|
||||
guess_hash = Blockchain.hash(current_block)
|
||||
|
||||
while guess_hash > target:
|
||||
# Check if we should still be mining
|
||||
# if not event.is_set():
|
||||
# raise Exception("STOP MINING")
|
||||
proof += 1
|
||||
return proof
|
||||
current_block['timestamp'] = datetime.now()
|
||||
current_block['proof'] += 1
|
||||
guess_hash = Blockchain.hash(current_block)
|
||||
|
||||
current_block['hash'] = guess_hash
|
||||
return current_block
|
||||
|
||||
|
||||
def miner(right, event):
|
||||
|
||||
while True:
|
||||
log.info(f'Waiting for task')
|
||||
latest_task = right.recv()
|
||||
log.info(f"Received new task for hash {latest_task['last_hash']} "
|
||||
f"with difficulty {latest_task['difficulty']}")
|
||||
task = right.recv()
|
||||
log.info(f"Received new mining task with difficulty {task['difficulty']}")
|
||||
|
||||
if latest_task:
|
||||
proof = proof_of_work(latest_task['last_hash'], latest_task['difficulty'], event)
|
||||
right.send({'proof': proof, 'last_hash': latest_task['last_hash']})
|
||||
if task:
|
||||
found_block = proof_of_work(task['block'], task['difficulty'], event)
|
||||
right.send({'found_block': found_block})
|
||||
|
|
2
node.py
2
node.py
|
@ -8,7 +8,7 @@ from tasks import initiate_node, mining_controller, peer_discovery
|
|||
|
||||
app = Sanic()
|
||||
|
||||
# reset_db()
|
||||
reset_db()
|
||||
initiate_node(app)
|
||||
|
||||
app.add_task(peer_discovery(app))
|
||||
|
|
27
tasks.py
27
tasks.py
|
@ -18,20 +18,20 @@ log = logging.getLogger('root.tasks')
|
|||
|
||||
def initiate_node(app):
|
||||
# Set up TCP Redirect (Port Forwarding)
|
||||
port_mapper = PortMapper()
|
||||
port_mapper.add_portmapping(8080, 8080, 'TCP', 'Electron')
|
||||
# port_mapper = PortMapper()
|
||||
# port_mapper.add_portmapping(8080, 8080, 'TCP', 'Electron')
|
||||
|
||||
# Set the identifier (unique Id) for our node
|
||||
node_identifier = get_config('node_identifier')
|
||||
if not node_identifier:
|
||||
node_identifier = set_config(key='node_identifier', value=uuid4().hex)
|
||||
|
||||
app.request_headers = {
|
||||
'content-type': 'application/json',
|
||||
'x-node-identifier': node_identifier,
|
||||
'x-node-ip': port_mapper.external_ip,
|
||||
'x-node-port': port_mapper.external_port,
|
||||
}
|
||||
# app.request_headers = {
|
||||
# 'content-type': 'application/json',
|
||||
# 'x-node-identifier': node_identifier,
|
||||
# 'x-node-ip': port_mapper.external_ip,
|
||||
# 'x-node-port': port_mapper.external_port,
|
||||
# }
|
||||
|
||||
log.info('Node Identifier: %s', node_identifier)
|
||||
|
||||
|
@ -120,7 +120,7 @@ async def mining_controller(app):
|
|||
process = multiprocessing.Process(target=miner, args=(right, event))
|
||||
process.start()
|
||||
|
||||
left.send({'last_hash': 123, 'difficulty': 6})
|
||||
left.send({'block': app.blockchain.build_block(), 'difficulty': 4})
|
||||
|
||||
while True:
|
||||
event.set()
|
||||
|
@ -134,9 +134,8 @@ async def mining_controller(app):
|
|||
|
||||
if left.poll():
|
||||
result = left.recv()
|
||||
proof = result['proof']
|
||||
previous_hash = result['last_hash']
|
||||
app.blockchain.new_block(proof, previous_hash)
|
||||
last_block_hash = app.blockchain.hash(app.blockchain.last_block)
|
||||
found_block = result['found_block']
|
||||
|
||||
left.send({'last_hash': last_block_hash, 'difficulty': 6})
|
||||
app.blockchain.save_block(found_block)
|
||||
|
||||
left.send({'block': app.blockchain.build_block(), 'difficulty': 4})
|
||||
|
|
|
@ -11,7 +11,7 @@ class BlockchainTestCase(TestCase):
|
|||
self.blockchain = Blockchain()
|
||||
|
||||
def create_block(self, proof=123, previous_hash='abc'):
|
||||
self.blockchain.new_block(proof, previous_hash)
|
||||
self.blockchain.build_block(proof, previous_hash)
|
||||
|
||||
def create_transaction(self, sender='a', recipient='b', amount=1):
|
||||
self.blockchain.new_transaction(
|
||||
|
|
Loading…
Reference in New Issue