67 lines
1.9 KiB
Python
67 lines
1.9 KiB
Python
import json
|
|
import os
|
|
|
|
from datetime import datetime
|
|
from hashlib import sha256, md5
|
|
|
|
|
|
class Blockchain(object):
|
|
def __init__(self):
|
|
self.chain = []
|
|
self.pending_transactions = []
|
|
|
|
# Create the genesis block
|
|
print("Creating genesis block")
|
|
|
|
self.new_block()
|
|
|
|
def new_block(self, previous_hash=None, nonce=None):
|
|
block = {
|
|
'index': len(self.chain),
|
|
'timestamp': datetime.utcnow().isoformat(),
|
|
'transactions': self.pending_transactions,
|
|
'previous_hash': previous_hash,
|
|
'nonce': nonce,
|
|
}
|
|
|
|
# Get the hash of this new block, and add it to the block
|
|
block["hash"] = self.hash(block)
|
|
|
|
print(f"Created block {block['index']}")
|
|
|
|
# Add the block to the chain
|
|
self.chain.append(block)
|
|
|
|
# Reset the list of pending transactions
|
|
self.pending_transactions = []
|
|
return block
|
|
|
|
@staticmethod
|
|
def hash(block):
|
|
# Transform the block dict into a json string with sorted keys
|
|
block_string = json.dumps(block, sort_keys=True).encode()
|
|
|
|
# ... and hash it
|
|
return sha256(block_string).hexdigest()
|
|
|
|
def last_block(self):
|
|
# Returns the last block in the chain (if there are blocks)
|
|
return self.chain[-1] if self.chain else None
|
|
|
|
@staticmethod
|
|
def pow_is_acceptable(hash_of_block, difficulty):
|
|
return hash_of_block[:difficulty] == "0" * difficulty
|
|
|
|
@staticmethod
|
|
def nonce():
|
|
return sha256(os.urandom(16)).hexdigest()
|
|
|
|
def proof_of_work(self, block=None, difficulty=4):
|
|
block = block or self.last_block()
|
|
|
|
while True:
|
|
block["nonce"] = self.nonce()
|
|
if self.pow_is_acceptable(hash_of_block=self.hash(block), difficulty=difficulty):
|
|
print(f"Block hash is {self.hash(block)} with random string {block['nonce']}")
|
|
return block
|