From 6d97f7f21b9db7e8913c230c34e0cd4211f64b38 Mon Sep 17 00:00:00 2001 From: MattGThomas Date: Wed, 15 Apr 2020 17:48:54 -0500 Subject: [PATCH 1/2] day 1 working --- .DS_Store | Bin 0 -> 6148 bytes basic_block_gp/blockchain.py | 93 +++++++++++++--- client_mining_p/blockchain.py | 198 ++++++++++++++++++++++++++++++++++ client_mining_p/miner.py | 12 ++- notes | 16 +++ 5 files changed, 302 insertions(+), 17 deletions(-) create mode 100644 .DS_Store create mode 100644 notes diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cd8c73ff657d40c040188aba270644fa9364f078 GIT binary patch literal 6148 zcmeHK&2AGh5FWQ_y9qzd0ThmVLE=!MtcvIXA%&14^?+1Z1P4IrZnC7SZr4$ELj)lx z7v2G00Esu@4R{b90KTye>?T2o3qs(D>~Cz(jO}mz6BChWkNX}`oroMH#!3y@3gLE6 zOR{Ea&H#mq(W5Sfl#ry}lC`a{3|I#KHU@a_p2waID5Ds=_Q(D`fj*$&K_Yu$B172m zA#nrg$`>Wjh%T0Va~dI1idX?+<%lR%X;q{T3z5pBm>@!eUxX;##ZidHg6}qZOVC?{ z-q`*WdaBZ3h4-7^^A0E3&@k{Z942v=H=Cct+InSUbIWmRPVHRnsedddejeoGtR0LV zbLlY1k3*wBkTUtM2jRmwnsn=n!9NNY9Yt zTQ6Ai3MpEjEn8zO`ldN4`>EAY^sCZ++Q;ZSbQid42D~DQ9w`0?VpfO4XMh#UfMvik zP-KA52N#L4WpJ)h9UaKj698C2wG!z3H-UXzgDrz|jc9=g?F!Vc!aOmAc1OKy>{^XJK9_LiLXLt}-Xl(r9zbfMuY}z`8Eh`TpPk`TM`@WJ{I-%fLUyfT(o* zP8(A)ck9yR_^x%4j*wW`Zmv Block "return": """ - # Use json.dumps to convert json into a string # Use hashlib.sha256 to create a hash # It requires a `bytes-like` object, which is what @@ -56,17 +61,18 @@ def hash(self, block): # or we'll have inconsistent hashes # TODO: Create the block_string - + string_object = json.dumps(block, sort_keys=True) + block_string = string_object.encode() # TODO: Hash this string using sha256 - + raw_hash = hashlib.sha256(block_string) # By itself, the sha256 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 of hexadecimal characters, which is # easier to work with and understand - + hash_string = raw_hash.hexdigest() # TODO: Return the hashed block string in hexadecimal format - pass + return hash_string @property def last_block(self): @@ -80,9 +86,12 @@ def proof_of_work(self, block): in an effort to find a number that is a valid proof :return: A valid proof for the provided block """ - # TODO - pass - # return proof + black_string = json.dumps(block, sort_keys=True) + proof = 0 + + while self.valid_proof(black_string, proof) is False: + proof += 1 + return proof @staticmethod def valid_proof(block_string, proof): @@ -96,8 +105,13 @@ def valid_proof(block_string, proof): correct number of leading zeroes. :return: True if the resulting hash is a valid proof, False otherwise """ - # TODO - pass + print(f'i will now check if {proof} is valid') + guess = block_string + str(proof) + guess = guess.encode() + hash_value = hashlib.sha256(guess).hexdigest() + print(hash_value) + return hash_value[:4] == '0000' + # return True or False @@ -111,23 +125,72 @@ def valid_proof(block_string, proof): blockchain = Blockchain() +@app.route('/', methods=['GET']) +def hello_world(): + response = { + 'text': 'hello world' + } + return jsonify(response), 200 + + @app.route('/mine', methods=['GET']) def mine(): # Run the proof of work algorithm to get the next proof - + print('we shall now mine a block!') + proof = blockchain.proof_of_work(blockchain.last_block) + print(f'after a long process, we got a value {proof}') # Forge the new Block by adding it to the chain with the proof + new_block = blockchain.new_block(proof) response = { - # TODO: Send a JSON response with the new block + 'block': new_block } return jsonify(response), 200 +# @app.route('/mine', methods=['POST']) +# def mine(): +# data = request.get_json() + +# if 'proof' not in data or 'id' not in data: +# response = { +# 'message': 'you are missing some values' +# } +# return jsonify(response), 400 +# new_proof = data.get('proof') + +# block_string = json.dumps(blockchain.last_block, sort_keys=True) +# if blockchain.valid_proof(block_string, new_proof): + +# previous_hash = blockchain.hash(blockchain.last_block) +# block = blockchain.new_block(new_proof, previous_hash) + +# response = { +# 'message': 'Success' +# } +# return jsonify(response), 200 + +# else: +# response = { +# 'message': 'Proof was missing, incorrect, or too late' +# } +# return jsonify(response), 400 + + @app.route('/chain', methods=['GET']) def full_chain(): response = { - # TODO: Return the chain and its current length + 'len': len(blockchain.chain), + 'chain': blockchain.chain + } + return jsonify(response), 200 + + +@app.route('/last_block', methods=['GET']) +def last_block(): + response = { + 'last_block': blockchain.chain[-1] } return jsonify(response), 200 diff --git a/client_mining_p/blockchain.py b/client_mining_p/blockchain.py index a0a26551..427efdc8 100644 --- a/client_mining_p/blockchain.py +++ b/client_mining_p/blockchain.py @@ -1,2 +1,200 @@ # 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 = [] + + # Create the genesis block + self.new_block(previous_hash=1, proof=100) + + def new_block(self, proof, previous_hash=None): + """ + Create a new Block in the Blockchain + + A block should have: + * Index + * Timestamp + * List of current transactions + * The proof used to mine this block + * The hash of the previous block + + :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.last_block) + } + + # Reset the current list of transactions + self.current_transactions = [] + # Append the chain to the block + self.chain.append(block) + # Return the new block + return block + + def hash(self, block): + """ + Creates a SHA-256 hash of a Block + + :param block": Block + "return": + """ + # Use json.dumps to convert json into a string + # Use hashlib.sha256 to create a hash + # It requires a `bytes-like` object, which is what + # .encode() does. + # It converts the Python string into a byte string. + # We must make sure that the Dictionary is Ordered, + # or we'll have inconsistent hashes + + # TODO: Create the block_string + string_object = json.dumps(block, sort_keys=True) + block_string = string_object.encode() + # TODO: Hash this string using sha256 + raw_hash = hashlib.sha256(block_string) + # By itself, the sha256 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 of hexadecimal characters, which is + # easier to work with and understand + hash_string = raw_hash.hexdigest() + # TODO: Return the hashed block string in hexadecimal format + return hash_string + + @property + def last_block(self): + return self.chain[-1] + + # def proof_of_work(self, block): + # """ + # Simple Proof of Work Algorithm + # Stringify the block and look for a proof. + # Loop through possibilities, checking each one against `valid_proof` + # in an effort to find a number that is a valid proof + # :return: A valid proof for the provided block + # """ + # black_string = json.dumps(block, sort_keys=True) + # proof = 0 + + # while self.valid_proof(black_string, proof) is False: + # proof += 1 + # return proof + + @staticmethod + def valid_proof(block_string, proof): + """ + Validates the Proof: Does hash(block_string, proof) contain 3 + 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 + """ + + guess = block_string + str(proof) + guess = guess.encode() + hash_value = hashlib.sha256(guess).hexdigest() + return hash_value[:7] == '000000' + + # return True or False + + +# 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('/', methods=['GET']) +def hello_world(): + response = { + 'text': 'hello world' + } + return jsonify(response), 200 + + +# @app.route('/mine', methods=['GET']) +# def mine(): +# # Run the proof of work algorithm to get the next proof +# print('we shall now mine a block!') +# proof = blockchain.proof_of_work(blockchain.last_block) +# print(f'after a long process, we got a value {proof}') +# # Forge the new Block by adding it to the chain with the proof +# new_block = blockchain.new_block(proof) + +# response = { +# 'block': new_block +# } + +# return jsonify(response), 200 + +@app.route('/mine', methods=['POST']) +def mine(): + data = request.get_json() + + if 'proof' not in data or 'id' not in data: + response = { + 'message': 'you are missing some values' + } + return jsonify(response), 400 + new_proof = data.get('proof') + + # block_string = json.dumps(blockchain.last_block, sort_keys=True) + # if blockchain.valid_proof(block_string, new_proof): + + # previous_hash = blockchain.hash(blockchain.last_block) + # block = blockchain.new_block(new_proof, previous_hash) + + # response = { + # 'message': 'Success' + # } + # return jsonify(response), 200 + + # else: + # response = { + # 'message': 'Proof was missing, incorrect, or too late' + # } + # return jsonify(response), 400 + + +@app.route('/chain', methods=['GET']) +def full_chain(): + response = { + 'len': len(blockchain.chain), + 'chain': blockchain.chain + } + return jsonify(response), 200 + + +@app.route('/last_block', methods=['GET']) +def last_block(): + response = { + 'last_block': blockchain.chain[-1] + } + 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 8e211b88..f1774c22 100644 --- a/client_mining_p/miner.py +++ b/client_mining_p/miner.py @@ -13,7 +13,12 @@ def proof_of_work(block): in an effort to find a number that is a valid proof :return: A valid proof for the provided block """ - pass + block_string = json.dumps(block, sort_keys=True) + proof = 0 + + while valid_proof(block_string, proof) is False: + proof += 1 + return proof def valid_proof(block_string, proof): @@ -27,7 +32,10 @@ def valid_proof(block_string, proof): correct number of leading zeroes. :return: True if the resulting hash is a valid proof, False otherwise """ - pass + guess = block_string + str(proof) + guess = guess.encode() + hash_value = hashlib.sha256(guess).hexdigest() + return hash_value[:7] == '000000' if __name__ == '__main__': diff --git a/notes b/notes new file mode 100644 index 00000000..a4397e61 --- /dev/null +++ b/notes @@ -0,0 +1,16 @@ +BLOCKCHAIN + what is a blockchain? + basically another data structure + offers trust that no one could have tampered with the data inside the blockchain + + kind of like a linked list + very often may want to use a linkedlist to make one + want to know the relationship between each block + [block] -> [block] -> [block] + + inside the block: + index, timestamp, transactions <- actual data, + a proof: just a number, but an important one + will also need hash of the previous block + + hash(block string + proof) = some value, usually 0's in the beginning \ No newline at end of file From 428230e7d9976337a7c6d6ff9c6051e6ba5d9b04 Mon Sep 17 00:00:00 2001 From: MattGThomas Date: Thu, 16 Apr 2020 18:35:46 -0500 Subject: [PATCH 2/2] mvp blockchain --- client_mining_p/blockchain.py | 79 +++++++++++++++++++++++++---------- client_mining_p/miner.py | 16 +++---- 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/client_mining_p/blockchain.py b/client_mining_p/blockchain.py index 427efdc8..145c6850 100644 --- a/client_mining_p/blockchain.py +++ b/client_mining_p/blockchain.py @@ -16,6 +16,23 @@ def __init__(self): # Create the genesis block self.new_block(previous_hash=1, proof=100) + ''' + :param sender: + :param recipient: + :param amount: + + :returns the index of the block that will hold the new transaction + ''' + + def new_transaction(self, sender, recipient, amount): + self.current_transactions.append({ + 'sender': sender, + 'recipient': recipient, + 'amount': amount + }) + + return self.last_block['index'] + 1 + def new_block(self, proof, previous_hash=None): """ Create a new Block in the Blockchain @@ -111,7 +128,7 @@ def valid_proof(block_string, proof): guess = block_string + str(proof) guess = guess.encode() hash_value = hashlib.sha256(guess).hexdigest() - return hash_value[:7] == '000000' + return hash_value[:3] == '000' # return True or False @@ -134,32 +151,34 @@ def hello_world(): return jsonify(response), 200 -# @app.route('/mine', methods=['GET']) -# def mine(): -# # Run the proof of work algorithm to get the next proof -# print('we shall now mine a block!') -# proof = blockchain.proof_of_work(blockchain.last_block) -# print(f'after a long process, we got a value {proof}') -# # Forge the new Block by adding it to the chain with the proof -# new_block = blockchain.new_block(proof) - -# response = { -# 'block': new_block -# } - -# return jsonify(response), 200 - @app.route('/mine', methods=['POST']) def mine(): data = request.get_json() - - if 'proof' not in data or 'id' not in data: + if 'id' and 'proof' not in data: response = { - 'message': 'you are missing some values' + 'message': 'you have missing values' } return jsonify(response), 400 - new_proof = data.get('proof') - + proof = data['proof'] + last_block = blockchain.last_block + block_string = json.dumps(last_block, sort_keys=True) + + if blockchain.valid_proof(block_string, proof): + blockchain.new_transaction( + sender='0', + recipient=data['id'], + amount=1 + ) + new_block = blockchain.new_block(proof) + response = { + 'block': new_block + } + return jsonify(response), 200 + else: + response = { + 'message': 'Proof is invalid' + } + return jsonify(response), 200 # block_string = json.dumps(blockchain.last_block, sort_keys=True) # if blockchain.valid_proof(block_string, new_proof): @@ -195,6 +214,24 @@ def last_block(): return jsonify(response), 200 +@app.route('/new_transaction', methods=['POST']) +def new_transaction(): + data = request.get_json() + + if 'recipient' and 'amount' and 'sender' not in data: + response = { + 'message': 'try again, you missed something' + } + return jsonify(response), 400 + + index = blockchain.new_transaction( + data['sender'], data['recipient'], data['amount']) + response = { + 'message': f'you can view this transaction on block {index}' + } + 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 f1774c22..577d02c4 100644 --- a/client_mining_p/miner.py +++ b/client_mining_p/miner.py @@ -35,7 +35,7 @@ def valid_proof(block_string, proof): guess = block_string + str(proof) guess = guess.encode() hash_value = hashlib.sha256(guess).hexdigest() - return hash_value[:7] == '000000' + return hash_value[:3] == '000' if __name__ == '__main__': @@ -45,6 +45,7 @@ def valid_proof(block_string, proof): else: node = "http://localhost:5000" + coins_mined = 0 # Load ID f = open("my_id.txt", "r") id = f.read() @@ -64,15 +65,16 @@ def valid_proof(block_string, proof): break # TODO: Get the block from `data` and use it to look for a new proof - # new_proof = ??? + new_proof = proof_of_work(data['last_block']) # When found, POST it to the server {"proof": new_proof, "id": id} post_data = {"proof": new_proof, "id": id} r = requests.post(url=node + "/mine", json=post_data) data = r.json() - - # TODO: If the server responds with a 'message' 'New Block Forged' - # add 1 to the number of coins mined and print it. Otherwise, - # print the message from the server. - pass + print(data) + if 'block' in data: + coins_mined += 1 + print(f'Total coins mined: {coins_mined}') + else: + print(data['message'])