From 65add51edc496dce1182598434811581895a2bab Mon Sep 17 00:00:00 2001 From: brettmadrid Date: Tue, 8 Oct 2019 21:14:08 -0700 Subject: [PATCH 1/2] completed blockchain.py --- basic_block_gp/blockchain.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/basic_block_gp/blockchain.py b/basic_block_gp/blockchain.py index 833d7d5..320952c 100644 --- a/basic_block_gp/blockchain.py +++ b/basic_block_gp/blockchain.py @@ -93,8 +93,12 @@ def proof_of_work(self, block): :return: A valid proof for the provided block """ # TODO - pass - # return proof + proof = 0 + + while self.valid_proof(block, proof) is False: + proof += 1 + + return proof @staticmethod def valid_proof(block_string, proof): @@ -109,8 +113,13 @@ def valid_proof(block_string, proof): :return: True if the resulting hash is a valid proof, False otherwise """ # TODO - pass + guess = f'{block_string}{proof}'.encode() + guess_hash = hashlib.sha256(guess).hexdigest() + + # return guess_hash[:6] == "000000" # return True or False + result = True if guess_hash[:6] == "000000" else False + return result def valid_chain(self, chain): """ @@ -131,9 +140,13 @@ def valid_chain(self, chain): print("\n-------------------\n") # Check that the hash of the block is correct # TODO: Return false if hash isn't correct + if block['previous_hash'] != self.hash(prev_block): + return False # Check that the Proof of Work is correct # TODO: Return false if proof isn't correct + if not self.valid_proof(prev_block['proof'], block['proof']): + return False prev_block = block current_index += 1 @@ -154,16 +167,23 @@ def valid_chain(self, chain): @app.route('/mine', methods=['GET']) def mine(): # We run the proof of work algorithm to get the next proof... - proof = blockchain.proof_of_work() + last_block = blockchain.last_block + last_proof = last_block['proof'] + proof = blockchain.proof_of_work(last_proof) # We must receive a reward for finding the proof. # TODO: # The sender is "0" to signify that this node has mine a new coin # The recipient is the current node, it did the mining! # The amount is 1 coin as a reward for mining the next block + blockchain.new_transaction( + sender='0', + recipient=node_identifier, + amount =1) # Forge the new Block by adding it to the chain # TODO + block = blockchain.new_block(proof, blockchain.hash(blockchain.last_block)) # Send a response with the new block response = { @@ -198,6 +218,8 @@ def new_transaction(): def full_chain(): response = { # TODO: Return the chain and its current length + 'currentChain': blockchain.chain, + 'length': len(blockchain.chain) } return jsonify(response), 200 From 5b38c54aad3051fb29c6b3248093c7e18cb4ae2a Mon Sep 17 00:00:00 2001 From: brettmadrid Date: Tue, 8 Oct 2019 21:19:26 -0700 Subject: [PATCH 2/2] completed miner.py --- client_mining_p/blockchain.py | 232 +++++++++++++++++++++++++++++++++- client_mining_p/miner.py | 23 +++- 2 files changed, 252 insertions(+), 3 deletions(-) diff --git a/client_mining_p/blockchain.py b/client_mining_p/blockchain.py index a0a2655..58df0fe 100644 --- a/client_mining_p/blockchain.py +++ b/client_mining_p/blockchain.py @@ -1,2 +1,230 @@ -# Paste your version of blockchain.py from the basic_block_gp -# folder here +import hashlib +import json +from time import time +from uuid import uuid4 + +from flask import Flask, jsonify, request + + +class Blockchain(object): + def __init__(self): + self.chain = [] + self.current_transactions = [] + self.nodes = set() + + self.new_block(previous_hash=1, proof=100) + + def new_block(self, proof, previous_hash=None): + """ + Create a new Block in the Blockchain + + :param proof: The proof given by the Proof of Work algorithm + :param previous_hash: (Optional) Hash of previous Block + :return: New Block + """ + + block = { + 'index': len(self.chain) + 1, + 'timestamp': time(), + 'transactions': self.current_transactions, + 'proof': proof, + 'previous_hash': previous_hash or self.hash(self.chain[-1]), + } + + # Reset the current list of transactions + self.current_transactions = [] + + self.chain.append(block) + return block + + def new_transaction(self, sender, recipient, amount): + """ + Creates a new transaction to go into the next mined Block + + :param sender: Address of the Recipient + :param recipient: Address of the Recipient + :param amount: Amount + :return: The index of the BLock that will hold this transaction + """ + + self.current_transactions.append({ + 'sender': sender, + 'recipient': recipient, + 'amount': amount, + }) + + return self.last_block['index'] + 1 + + @staticmethod + def hash(block): + """ + Creates a SHA-256 hash of a Block + + :param block": Block + "return": + """ + + + # json.dumps converts json into a string + # hashlib.sha246 is used to createa hash + # It requires a `bytes-like` object, which is what + # .encode() does. It convertes the string to bytes. + # We must make sure that the Dictionary is Ordered, + # or we'll have inconsistent hashes + + block_string = json.dumps(block, sort_keys=True).encode() + + # By itself, this function returns the hash in a raw string + # that will likely include escaped characters. + # This can be hard to read, but .hexdigest() converts the + # hash to a string using hexadecimal characters, which is + # easer to work with and understand. + return hashlib.sha256(block_string).hexdigest() + + @property + def last_block(self): + return self.chain[-1] + + def proof_of_work(self, block): + """ + Simple Proof of Work Algorithm + Find a number p such that hash(last_block_string, p) contains 6 leading + zeroes + :return: A valid proof for the provided block + """ + # TODO + proof = 0 + + while self.valid_proof(block, proof) is False: + proof += 1 + + return proof + + @staticmethod + def valid_proof(block_string, proof): + """ + Validates the Proof: Does hash(block_string, proof) contain 6 + leading zeroes? Return true if the proof is valid + :param block_string: The stringified block to use to + check in combination with `proof` + :param proof: The value that when combined with the + stringified previous block results in a hash that has the + correct number of leading zeroes. + :return: True if the resulting hash is a valid proof, False otherwise + """ + # TODO + guess = f'{block_string}{proof}'.encode() + guess_hash = hashlib.sha256(guess).hexdigest() + + # return guess_hash[:6] == "000000" + # return True or False + result = True if guess_hash[:6] == "000000" else False + return result + + def valid_chain(self, chain): + """ + Determine if a given blockchain is valid. We'll need this + later when we are a part of a network. + + :param chain: A blockchain + :return: True if valid, False if not + """ + + prev_block = chain[0] + current_index = 1 + + while current_index < len(chain): + block = chain[current_index] + print(f'{prev_block}') + print(f'{block}') + print("\n-------------------\n") + # Check that the hash of the block is correct + # TODO: Return false if hash isn't correct + if block['previous_hash'] != self.hash(prev_block): + return False + + # Check that the Proof of Work is correct + # TODO: Return false if proof isn't correct + if not self.valid_proof(prev_block['proof'], block['proof']): + return False + + prev_block = block + current_index += 1 + + return True + + +# Instantiate our Node +app = Flask(__name__) + +# Generate a globally unique address for this node +node_identifier = str(uuid4()).replace('-', '') + +# Instantiate the Blockchain +blockchain = Blockchain() + + +@app.route('/mine', methods=['GET']) +def mine(): + # We run the proof of work algorithm to get the next proof... + last_block = blockchain.last_block + last_proof = last_block['proof'] + proof = blockchain.proof_of_work(last_proof) + + # We must receive a reward for finding the proof. + # TODO: + # The sender is "0" to signify that this node has mine a new coin + # The recipient is the current node, it did the mining! + # The amount is 1 coin as a reward for mining the next block + blockchain.new_transaction( + sender='0', + recipient=node_identifier, + amount =1) + + # Forge the new Block by adding it to the chain + # TODO + block = blockchain.new_block(proof, blockchain.hash(blockchain.last_block)) + + # Send a response with the new block + response = { + 'message': "New Block Forged", + 'index': block['index'], + 'transactions': block['transactions'], + 'proof': block['proof'], + 'previous_hash': block['previous_hash'], + } + return jsonify(response), 200 + + +@app.route('/transactions/new', methods=['POST']) +def new_transaction(): + values = request.get_json() + + # Check that the required fields are in the POST'ed data + required = ['sender', 'recipient', 'amount'] + if not all(k in values for k in required): + return 'Missing Values', 400 + + # Create a new Transaction + index = blockchain.new_transaction(values['sender'], + values['recipient'], + values['amount']) + + response = {'message': f'Transaction will be added to Block {index}'} + return jsonify(response), 201 + + +@app.route('/chain', methods=['GET']) +def full_chain(): + response = { + # TODO: Return the chain and its current length + 'currentChain': blockchain.chain, + 'length': len(blockchain.chain) + } + return jsonify(response), 200 + + +# Run the program on port 5000 +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) + diff --git a/client_mining_p/miner.py b/client_mining_p/miner.py index f0e8d1b..4556d69 100644 --- a/client_mining_p/miner.py +++ b/client_mining_p/miner.py @@ -5,7 +5,16 @@ # TODO: Implement functionality to search for a proof +def valid_proof(last_proof, proof): + guess = f'{last_proof}{proof}'.encode() + guess_hash = hashlib.sha256(guess).hexdigest() + return guess_hash[:6] == "000000" +def proof_of_work(last_proof): + proof = 0 + while valid_proof(last_proof, proof) is False: + proof +=1 + return proof if __name__ == '__main__': # What node are we interacting with? @@ -24,4 +33,16 @@ # TODO: If the server responds with 'New Block Forged' # add 1 to the number of coins mined and print it. Otherwise, # print the message from the server. - pass + last = requests.get(url=node + "/last_proof") + last_proof = last.json()['proof'] + print('last proof', last_proof) + data = { + 'proof': proof_of_work(last_proof) + } + print('next proof', data['proof']) + res = requests.post(url = f'{node}/mine', json = data) + + if (res.json()['index']): + coins_mined+=1 + print(f'Total Coins Mined: {coins_mined}') + print(res.json()['message'])