blockchain/blockchain.py

119 lines
3.2 KiB
Python
Raw Normal View History

2017-12-28 21:54:57 +01:00
import logging
2017-12-25 18:12:25 +01:00
from datetime import datetime
2017-09-24 21:40:01 +02:00
2017-12-29 20:32:55 +01:00
from database import Block, db
2017-12-29 21:38:57 +01:00
from helpers import hash_block
2017-12-28 21:52:11 +01:00
logger = logging.getLogger('root.blockchain')
2017-09-24 21:40:01 +02:00
2017-10-05 11:20:56 +02:00
class Blockchain:
2017-09-24 21:40:01 +02:00
def __init__(self):
self.current_transactions = []
2017-12-25 18:12:25 +01:00
self.difficulty = 4
2017-09-24 21:40:01 +02:00
2017-12-29 21:38:57 +01:00
# Create the genesis block if it doesn't exist
2017-12-29 19:52:43 +01:00
if not self.last_block:
block = self.build_block()
2017-12-29 21:38:57 +01:00
block['hash'] = hash_block(block)
2017-12-29 19:52:43 +01:00
self.save_block(block)
2017-12-28 21:52:11 +01:00
logger.info("✨ Created genesis block")
logger.info("Blockchain Initiated")
2017-09-24 21:40:01 +02:00
2017-12-28 21:52:11 +01:00
@staticmethod
def get_blocks(height=0):
2017-09-24 21:40:01 +02:00
"""
2017-12-25 18:12:25 +01:00
Returns all blocks from a given height
2017-09-24 21:40:01 +02:00
2017-12-25 18:12:25 +01:00
:param height: <int> The height from which to return blocks
:return:
2017-09-24 21:40:01 +02:00
"""
2017-12-25 18:12:25 +01:00
blocks = db.query(Block).filter(Block.height >= height).all()
return [block.to_dict() for block in blocks]
2017-09-24 21:40:01 +02:00
def valid_chain(self, chain):
2017-09-24 21:40:01 +02:00
"""
2017-12-25 18:12:25 +01:00
Determines if a given blockchain is valid
2017-09-24 21:40:01 +02:00
2017-10-05 11:20:56 +02:00
:param chain: A blockchain
:return: True if valid, False if not
2017-09-24 21:40:01 +02:00
"""
last_block = chain[0]
current_index = 1
while current_index < len(chain):
block = chain[current_index]
2017-12-25 18:12:25 +01:00
2017-12-29 21:38:57 +01:00
# Check that the hash_block of the block is correct
if block['previous_hash'] != self.hash_block(last_block):
2017-09-24 21:40:01 +02:00
return False
# Check that the Proof of Work is correct
if not self.valid_proof(last_block['proof'], block['proof']):
return False
last_block = block
current_index += 1
return True
2017-12-29 19:52:43 +01:00
def build_block(self):
2017-09-24 21:40:01 +02:00
"""
Create a new Block in the Blockchain
"""
2017-12-29 19:52:43 +01:00
last_block = self.last_block
2017-09-24 21:40:01 +02:00
2017-12-29 19:52:43 +01:00
return {
2017-12-29 20:32:55 +01:00
'height': last_block.height + 1 if last_block else 0,
'timestamp': datetime.utcnow(),
2017-12-29 19:52:43 +01:00
'transactions': self.current_transactions,
'previous_hash': last_block.hash if last_block else 0,
2017-12-29 20:32:55 +01:00
'proof': -1,
2017-12-29 19:52:43 +01:00
}
2017-12-25 18:12:25 +01:00
2017-12-29 19:52:43 +01:00
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:
"""
2017-12-29 22:37:10 +01:00
print(block_dict)
2017-12-29 19:52:43 +01:00
block = Block(**block_dict)
2017-12-25 18:12:25 +01:00
db.add(block)
db.commit()
2017-09-24 21:40:01 +02:00
# Reset the current list of transactions
self.current_transactions = []
def new_transaction(self, sender, recipient, amount):
2017-09-24 21:40:01 +02:00
"""
Creates a new transaction to go into the next mined Block
2017-10-05 11:20:56 +02:00
:param sender: Address of the Sender
:param recipient: Address of the Recipient
:param amount: Amount
:return: The index of the Block that will hold this transaction
2017-09-24 21:40:01 +02:00
"""
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount,
})
return self.last_block['index'] + 1
@property
def last_block(self):
2017-12-25 18:12:25 +01:00
"""
Returns the last block in the Blockchain (greatest height)
:return: <Block>
"""
return db.query(Block).order_by(Block.height.desc()).first()