Skip to content

Commit

Permalink
GeneralCoin is now part of the package.
Browse files Browse the repository at this point in the history
  • Loading branch information
Nevavuori Petteri committed May 17, 2018
1 parent 633aa88 commit ce7a322
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 1 deletion.
117 changes: 116 additions & 1 deletion notebooks/blockchain/apps.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import threading

import requests
Expand Down Expand Up @@ -75,7 +76,7 @@ def api_blocks(self):

if request.method == 'GET':

response = {'blockchain': self.chain.chain,
response = {'chain': self.chain.chain,
'length': len(self.chain.chain)}

return (jsonify(response), 200)
Expand Down Expand Up @@ -113,3 +114,117 @@ def stop(self):
if self.thread.is_alive():

return requests.get(f'{self.host_url}/shutdown')


class GeneralCoinApp(BlockchainApp):

def __init__(self, host='localhost', port=5000, chain=chains.GeneralCoin):
super().__init__(host, port, chain)

def add_api_endpoints(self):
"Add API endpoints to the Flask WebApp."

self.app.add_url_rule(
rule='/blocks',
view_func=self.api_blocks,
methods=['GET', 'POST']
)
self.app.add_url_rule(
rule='/blocks/validate',
view_func=self.api_validate,
)
self.app.add_url_rule(
rule='/transactions',
view_func=self.api_transactions,
methods=['POST']
)
self.app.add_url_rule(
rule='/nodes',
view_func=self.api_nodes,
methods=['POST']
)
self.app.add_url_rule(
rule='/nodes/chains',
view_func=self.api_chains
)
self.app.add_url_rule(
rule='/shutdown',
view_func=self.api_shutdown,
)

def api_blocks(self):
"""Either retrieve the node's current chain or post a new block
to the chain with a mining reward from the GeneralCoin coinbase."""

if request.method == 'GET':

response = {'chain': self.chain.chain,
'length': len(self.chain.chain)}

return (json.dumps(response), 200)

if request.method == 'POST':

prev_block = self.chain.get_previous_block()
prev_hash = self.chain.hash_block(prev_block)
prev_proof = prev_block['proof']
proof = self.chain.proof_of_work(prev_proof)

self.chain.add_transaction(
sender='GC Coinbase',
receiver=self.chain.node_address,
amount=10)

block = self.chain.create_block(proof, prev_hash)

response = {'message': 'Congratulations, you just mined a Block!',
'block': block}

return (jsonify(response), 200)

def api_transactions(self):
"Post a new transaction to the node provided as a JSON-dictionary."

request_json = request.get_json()

if not all(key in request_json for key in ['sender', 'receiver', 'amount']):

return jsonify({'message': f'Bad transaction data: {request_json}'}), 400

next_block = self.chain.add_transaction(
sender=request_json['sender'],
receiver=request_json['receiver'],
amount=request_json['amount'])

return jsonify({'message': f'The transaction will be added to Block {next_block}',
'transaction': request_json}), 200

def api_nodes(self):
"Connect nodes in a list of nodes to the network"

request_json = request.get_json()

if not 'nodes' in request_json or len(request_json['nodes']) == 0:

return jsonify({'message': 'No node data'}), 400

for node in request_json['nodes']:

self.chain.add_node(node)

return jsonify({'message': f'Nodes connected', 'nodes': list(self.chain.nodes)}), 200

def api_chains(self):
"Query the nodes for their chains and possibly replace node's chain with the longest one"

replaced = self.chain.replace_chain()

if replaced:

response = {'message': 'Chain of the Node was replaced'}

else:

response = {'message': 'Chain of the Node was the longest'}

return (jsonify(response), 200)
76 changes: 76 additions & 0 deletions notebooks/blockchain/chains.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import datetime
import hashlib
import json
from urllib.parse import urlparse
from uuid import uuid4

import requests


class Blockchain:
Expand Down Expand Up @@ -77,3 +81,75 @@ def is_chain_valid(self, chain):
return False

return True


class GeneralCoin(Blockchain):

def __init__(self):

self.node_address = str(uuid4()).replace('-', '')
self.transactions = []
self.nodes = set()
super().__init__()

def create_block(self, proof, previous_hash):
"Create a block with new transactions."

block = {
'index': len(self.chain),
'timestamp': str(datetime.datetime.now()),
'proof': proof,
'previous_hash': previous_hash,
'transactions': self.transactions
}
self.transactions = []
self.chain.append(block)

return block

def add_transaction(self, sender, receiver, amount):
"Add a transaction to the list of transactions."

self.transactions.append(
{'sender': sender,
'receiver': receiver,
'amount': amount})

return self.get_previous_block()['index'] + 1

def add_node(self, address):
"Add a node to the GeneralCoin network."

parsed_url = urlparse(address)
self.nodes.add(parsed_url.netloc)

def replace_chain(self):
"Scan the network for longest chain and replace the current accordingly."

longest_chain = None
longest_chain_length = len(self.chain)

for node in self.nodes:

response = requests.get(f'http://{node}/blocks')

if not response.status_code == 200:

print(f'Bad response from {node}: {response.status_code}')
continue

node_chain = response.json()['chain']
node_chain_length = response.json()['length']

if node_chain_length > longest_chain_length and self.is_chain_valid(node_chain):

longest_chain_length = node_chain_length
longest_chain = node_chain

if longest_chain is not None:

self.chain = longest_chain

return True

return False

0 comments on commit ce7a322

Please sign in to comment.