#!/usr/bin/env python # Source https://gist.github.com/aunyks/47d157f8bc7d1829a729c2a6a919c173 # Modifications: # - Can pass port number as command line argument. # - Added GET method /add_peer. # - On retrieving blockchain, call consensus to synchronize with other peers. # - On updating the current blockchain from a peers' blockchain, convert list # of JSON blocks to native Block objects. import datetime as date import hashlib as hasher import json import requests import sys from flask import Flask from flask import request node = Flask(__name__) # Define what a Snakecoin block is. 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.hash_block() def hash_block(self): sha = hasher.sha256() sha.update(self.attributes()) return sha.hexdigest() def attributes(self): return str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash) def create_genesis_block(): return Block(0, date.datetime.now(), { "proof-of-work": 9, "transactions": None, }, "0") miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi" blockchain = [] peer_nodes = [] mining = True # Transaction submit. transactions = [] @node.route('/transaction', methods=['POST']) def transaction(): if request.method == 'POST': transaction = request.get_json() transactions.append(transaction) print "New transaction" print "FROM: {}".format(transaction['from']) print "TO: {}".format(transaction['to']) print "AMOUNT: {}\n".format(transaction['amount']) return "Transaction submission successful\n" @node.route('/blocks', methods=['GET']) def get_blocks(): ret = [] for block in consensus(): ret.append({ "index": str(block.index), "timestamp": str(block.timestamp), "data": str(block.data), "hash": block.hash, }) return json.dumps(ret) # Update the current blockchain to the longest blockchain across all other # peer nodes. def consensus(): global blockchain longest_chain = blockchain for chain in find_other_chains(): if len(longest_chain) < len(chain): longest_chain = chain return update_blockchain(longest_chain) # Updates current blockchain. If updated is needed, converts JSON blockchain to # list of blocks. def update_blockchain(src): if len(src) <= len(blockchain): return blockchain ret = [] for b in src: ret.append(Block(b['index'], b['timestamp'], b['data'], b['hash'])) return ret def find_other_chains(): ret = [] for peer in peer_nodes: response = requests.get('http://%s/blocks' % peer) if response.status_code == 200: print("blocks from peer: " + response.content) ret.append(json.loads(response.content)) return ret @node.route('/add_peer', methods=['GET']) def add_peer(): host = request.args['host'] if 'host' in request.args else 'localhost' port = request.args['port'] peer = host + ':' + port peer_nodes.append(peer) print("Peer added: %s" % peer) return "" @node.route('/mine', methods = ['GET']) def mine(): last_block = blockchain[len(blockchain) - 1] last_proof = last_block.data['proof-of-work'] proof = proof_of_work(last_proof) transactions.append( {"from": "network", "to": miner_address, "amount": 1} ) data = { "proof-of-work": proof, "transactions": list(transactions) } index = last_block.index + 1 timestamp = date.datetime.now() # Empty transactions list. transactions[:] = [] # Create a mined block. block = Block(index, timestamp, data, last_block.hash) blockchain.append(block) return json.dumps({ "index": index, "timestamp": str(timestamp), "data": data, "hash": last_block.hash }) + "\n" def proof_of_work(last_proof): incrementor = last_proof + 1 while not (incrementor % 9 == 0 and incrementor % last_proof == 0): incrementor += 1 return incrementor def main(): port = 5000 if len(sys.argv) > 1: port = sys.argv[1] blockchain.append(create_genesis_block()) node.run(port=port) if __name__ == '__main__': main()