blockchain/mining.py

76 lines
2.2 KiB
Python
Raw Permalink Normal View History

2017-12-29 21:38:57 +01:00
import asyncio
2017-12-28 21:52:11 +01:00
import logging
2017-12-29 21:38:57 +01:00
import multiprocessing
2017-12-29 19:52:43 +01:00
from datetime import datetime
2017-12-28 21:52:11 +01:00
2017-12-29 21:38:57 +01:00
from helpers import hash_block
from tasks import we_should_still_be_mining
2017-12-28 21:52:11 +01:00
2017-12-29 19:52:43 +01:00
log = logging.getLogger('root.mining')
2017-12-28 21:52:11 +01:00
2017-12-29 19:52:43 +01:00
def proof_of_work(current_block, difficulty, event):
2017-12-28 21:52:11 +01:00
"""
Simple Proof of Work Algorithm
2017-12-29 19:52:43 +01:00
:param current_block: The partially complete block currently being mined
2017-12-28 21:52:11 +01:00
: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")
2017-12-29 21:38:57 +01:00
guess_hash = hash_block(current_block)
2017-12-29 19:52:43 +01:00
while guess_hash > target:
2017-12-28 21:52:11 +01:00
# Check if we should still be mining
# if not event.is_set():
# raise Exception("STOP MINING")
2017-12-29 20:32:55 +01:00
current_block['timestamp'] = datetime.utcnow()
2017-12-29 19:52:43 +01:00
current_block['proof'] += 1
2017-12-29 21:38:57 +01:00
guess_hash = hash_block(current_block)
2017-12-29 19:52:43 +01:00
current_block['hash'] = guess_hash
return current_block
2017-12-28 21:52:11 +01:00
2017-12-29 21:14:09 +01:00
def miner(pipe, event):
2017-12-28 21:52:11 +01:00
while True:
2017-12-29 21:14:09 +01:00
task = pipe.recv()
2017-12-29 21:38:57 +01:00
log.debug(f"Received new mining task with difficulty {task['difficulty']}")
2017-12-29 19:52:43 +01:00
if task:
found_block = proof_of_work(task['block'], task['difficulty'], event)
2017-12-29 21:14:09 +01:00
pipe.send({'found_block': found_block})
2017-12-29 21:38:57 +01:00
async def mining_controller(app):
pipe, remote_pipe = multiprocessing.Pipe()
event = multiprocessing.Event()
# Spawn a new process consisting of the miner() function
# and send the right end of the pipe to it
process = multiprocessing.Process(target=miner, args=(remote_pipe, event))
process.start()
pipe.send({'block': app.blockchain.build_block(), 'difficulty': 5})
while True:
event.set()
# We'll check the pipe every 100 ms
await asyncio.sleep(0.1)
# Check if we should still be mining
if not we_should_still_be_mining():
event.clear()
if pipe.poll():
result = pipe.recv()
found_block = result['found_block']
app.blockchain.save_block(found_block)
log.info(f"Mined Block {found_block['height']} containing {len(found_block['transactions'])} transactions")
pipe.send({'block': app.blockchain.build_block(), 'difficulty': 5})