Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 68 additions & 73 deletions basic_block_gp/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import json
from time import time
from uuid import uuid4

from flask import Flask, jsonify, request


Expand All @@ -15,90 +14,53 @@ def __init__(self):
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: <int> The proof given by the Proof of Work algorithm
:param previous_hash: (Optional) <str> Hash of previous Block
:return: <dict> New Block
"""

block = {
# TODO
'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
# Append the chain to the block
self.current_transactions = []
# Append the block to chain
self.chain.append(block)
# Return the new block
pass
return self.chain

def hash(self, block):
"""
Creates a SHA-256 hash of a Block

:param block": <dict> Block
"return": <str>
"""

# 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

# TODO: Hash this string using sha256
# Use .encode() to convert Python string into a BYTE string.
# Sort dict keys or we'll have inconsistent hashes

# 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
string_block = json.dumps(block, sort_keys=True)
raw_hash = hashlib.sha256(string_block.encode())


# TODO: Return the hashed block string in hexadecimal format
pass
# The sha256 function returns the hash in a raw string that includes escaped characters.
# This can be hard to read, but .hexdigest() converts hash to a string of hexadecimal characters
hex_hash = raw_hash.hexdigest()
return hex_hash

@property
def last_block(self):
@property #acts like property, not method - don't have to use ()
def last_block(self): #O(1)
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
"""
# TODO
pass
# return proof
# def proof_of_work(self, block):
# block_string = json.dumps(block, sort_keys=True)
# proof = 0
# while self.valid_proof(block_string, proof): #until num proof is found
# 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: <string> The stringified block to use to
check in combination with `proof`
:param proof: <int?> 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
pass
# return True or False
guess = f'{block_string}{proof}'
guess_hash = hashlib.sha256(guess).hexdigest()

return guess_hash[:6] =='000000'


# Instantiate our Node
Expand All @@ -111,23 +73,56 @@ def valid_proof(block_string, proof):
blockchain = Blockchain()


@app.route('/mine', methods=['GET'])
@app.route('/mine', methods=['POST'])
def mine():
# Run the proof of work algorithm to get the next proof
# Handle non json request
values = request.get_json()

required = ['proof', 'id']
if not all(k in values for k in required): #nested for loop, no problem, it's small, O(2n) is constant, linear
response = {'message': 'Missing values'}
return jsonify(response), 400

submitted_proof = values['proof']

block_string = json.dumps(blockchain.last_block, sort_keys=True)
if blockchain.valid_proof(block_string, submitted_proof):
#forge new block by adding ...the proof
previous_hash = blockchain.hash(blockchain.last_block)
block = blockchain.new_block(proof, previous_hash)
else:
response={
'message': 'Proof was invalid or late'
}
return jsonify(response), 200

# Run the proof of work algorithm to get the next proof
proof = blockchain.proof_of_work(blockchain.last_block)
# Forge the new Block by adding it to the chain with the proof
previous_hash = blockchain.hash(blockchain.last_block)
block = blockchain.new_block(proof, previous_hash)

response = {
# TODO: Send a JSON response with the new block
}
'new_block': block
} #dictionary for response because similar keys:values data structure

return jsonify(response), 200


@app.route('/chain', methods=['GET'])
def full_chain():
response = {
# TODO: Return the chain and its current length
'message': 'hello'
'chain': blockchain.chain,
'length': len(blockchain.chain)'
}
return jsonify(response), 200

# Add an endpoint called `last_block` that returns the last block in the chain
@app.route('/last_block', methods=['GET'])
def return_last_block():
response = {
'last_block': blockchain.last_block
}
return jsonify(response), 200

Expand Down
152 changes: 150 additions & 2 deletions basic_transactions_gp/blockchain.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,150 @@
# Paste your version of blockchain.py from the client_mining_p
# 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):
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 block to chain
self.chain.append(block)
# Return the new block
return self.chain

def hash(self, block):
# Use json.dumps to convert json into a string
# Use hashlib.sha256 to create a hash
# Use .encode() to convert Python string into a BYTE string.
# Sort dict keys or we'll have inconsistent hashes

string_block = json.dumps(block, sort_keys=True)
raw_hash = hashlib.sha256(string_block.encode())


# The sha256 function returns the hash in a raw string that includes escaped characters.
# This can be hard to read, but .hexdigest() converts hash to a string of hexadecimal characters
hex_hash = raw_hash.hexdigest()
return hex_hash

@property #acts like property, not method - don't have to use ()
def last_block(self): #O(1)
return self.chain[-1]

@staticmethod
def valid_proof(block_string, proof):
guess = f'{block_string}{proof}'
guess_hash = hashlib.sha256(guess).hexdigest()

return guess_hash[:6] =='000000'

def new_transaction(self, sender, recipient, amount): #can't be static, we adding things to current trasnactions
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount
})
#last block is cement, unchangeable, so newest/latest bloack is...
return self.last_block['index'] + 1


# 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=['POST'])
def mine():
# Handle non json request
values = request.get_json()

required = ['proof', 'id']
if not all(k in values for k in required): #nested for loop, no problem, it's small, O(2n) is constant, linear
response = {'message': 'Missing values'}
return jsonify(response), 400

submitted_proof = values['proof']

block_string = json.dumps(blockchain.last_block, sort_keys=True)
if blockchain.valid_proof(block_string, submitted_proof):

blockchain.new_transaction('0', values['id'], 1)

#forge new block by adding ...the proof
previous_hash = blockchain.hash(blockchain.last_block)
block = blockchain.new_block(proof, previous_hash)
else:
response={
'message': 'Proof was invalid or late'
}
return jsonify(response), 200

# Run the proof of work algorithm to get the next proof
proof = blockchain.proof_of_work(blockchain.last_block)
# Forge the new Block by adding it to the chain with the proof
previous_hash = blockchain.hash(blockchain.last_block)
block = blockchain.new_block(proof, previous_hash)

response = {
'new_block': block
} #dictionary for response because similar keys:values data structure

return jsonify(response), 200


@app.route('/chain', methods=['GET'])
def full_chain():
response = {
'message': 'hello'
'chain': blockchain.chain,
'length': len(blockchain.chain)'
}
return jsonify(response), 200

# Add an endpoint called `last_block` that returns the last block in the chain
@app.route('/last_block', methods=['GET'])
def return_last_block():
response = {
'last_block': blockchain.last_block
}
return jsonify(response), 200

@app.route('/transactions/new', methods=['POST'])
def recieve_transactions():
values = request.get_json()
#breakpoint()
required=['sender', 'recipient', 'amount']
if not all(k in values for k in required):
response = {'message': 'Missing values'}
return jsonify(response), 400
index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
response = {
'message': f'Transaction will be added to block{amount}.'
}
return jsonify(response), 201

# Run the program on port 5000
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
7 changes: 6 additions & 1 deletion basic_wallet_p/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ This app should:

Stretch Goals:
* Use styling to visually distinguish coins sent and coins received
* Paginate the list of transactions if there are more than ten
* Paginate the list of transactions if there are more than ten

TO START SERVER:
1. pip install flask
2. export FLASK_APP=flask_homepg.py
3. flask run
Loading