Skip to content

Instantly share code, notes, and snippets.

@meganehouser
Created December 29, 2017 14:24
Show Gist options
  • Save meganehouser/341b1f2e3ba56d92ebf1dc57ceca22da to your computer and use it in GitHub Desktop.
Save meganehouser/341b1f2e3ba56d92ebf1dc57ceca22da to your computer and use it in GitHub Desktop.
# coding: utf-8
import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from urllib.parse import urlparse
from flask import Flask, jsonify, request
data Transaction(sender: str, recipient: str, amount: int)
data Block(index: int,
timestamp: float,
transactions: List[transactions],
proof: int,
previous_hash: str)
data BlockChain(chain:list, current_transactions:List[Transaction], nodes):
@classmethod
def create(self):
return BlockChain.add_block(BlockChain([], [], set()), proof=100, previous_hash=1)
@property
def last_block(self):
return self.chain[-1]
@property
def next_index(self):
return self.last_block.index + 1
@classmethod
def register_node(self, BlockChain(chain, trans, nodes), address):
parsed_url = parse(address)
nodes.add(parsed_url.netloc)
return BlockChain(chain, trans, nodes)
@classmethod
def add_block(self, BlockChain(chain, trans, nodes), proof, previous_hash=None):
"""
ブロックチエーンに新しいブロックを作る
"""
index = len(chain) + 1
p_hash = previous_hash or BlockChain.hash(chain[-1])
block = Block(index, time(), trans, proof, p_hash)
chain.append(block)
return BlockChain(chain, [], nodes)
@classmethod
def add_transaction(self, BlockChain(c, curr, nodes), sender, recipient, amount):
"""
次に採掘されるブロックに加える新しいトランザクションをつくる
"""
tran = Transaction(sender, recipient, amount)
curr.append(tran)
return BlockChain(c, curr, nodes)
@classmethod
def hash(self, block):
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
def valid_chain(chain):
"""
ブロックチェーンが正しいかを確認する
"""
last_block = chain[0]
current_index = 1
while current_index < len(chain):
block = chain[current_index]
print(last_block)
print(block)
print("\n----------------\n")
if block.previous_hash != BlockChain.hash(last_block)
return False
if not valid_proof(last_block.proof, block.proof)
return False
last_block = block
current_index += 1
return True
def resolve_conflicts(BlockChain(chain, trans, nodes)):
"""
ネットワーク上の最も長いチェーンで自らを置き換えることでコンフリクトを解決する
"""
neighbours = nodes
new_chain = None
max_length = len(chian)
for node in neighbours:
response = request.get("http://{}/chain".format(node))
if response.status_code == 200:
length = response.json()['length']
chain = response.json()['chain']
if length > max_length and valid_chain(chain):
max_length = length
new_chain = chain
if new_chain:
chain = new_chain
return True, BlockChain(chain, trans, nodes)
return False, BlockChain(chain, trans, nodes)
def proof_of_work(last_proof):
"""
プルーフ・オブ・ワークのアルゴリズム
- hash(pp')の最初の4つが0となるような p' を探す
- p は前のプルーフ、p'は新しいプルーフ
"""
proof = 0
while valid_proof(last_proof, proof) is False:
proof += 1
return proof
def valid_proof(last_proof, proof):
"""
プルーフが正しいか確認する
"""
guess = "{}{}".format(last_proof, proof).encode()
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4] == "0000"
app = Flask(__name__)
node_identifier = str(uuid4()).replace('-', '')
blockchain = BlockChain.create()
@app.route('/transactions/new', methods=['POST'])
def new_transactions():
values = request.get_json()
required = ['sender', 'recipient', 'amount']
if not all(k in values for k in required):
return 'Missing values', 400
global blockchain
blockchain = BlockChain.add_transaction(blockchain, values['sender'], values['recipient'], values['amount'])
index = blockchain.next_index
response = {'message': "トランザクションは{}に追加されました。".format(index)}
return jsonify(response), 201
@app.route('/mine', methods=['GET'])
def mine():
global blockchain
last_block = blockchain.last_block
last_proof = last_block.proof
proof = proof_of_work(last_proof)
blockchain = BlockChain.add_transaction(blockchain, sender="0",
recipient=node_identifier,
amount=1)
blockchain = BlockChain.add_block(blockchain, proof)
block = blockchain.last_block
response = {
'message': "新しいブロックを採掘しました",
'index': block.index,
'transactions': block.transactions,
'proof': block.proof,
'previous_hash': block.previous_hash,
}
return jsonify(response), 200
@app.route('/chain', methods=['GET'])
def full_chian():
global blockchain
response = {
'chain': blockchain.chain,
'length': len(blockchain.chain),
}
return jsonify(response), 200
@app.route('/node/register', methods=['POST'])
def register_node():
global blockchain
values = request.get_json()
nodes = values.get('nodes')
if nodes is None:
return "Error: 有効ではないノードのリストです", 400
for node in nodes:
blockchain = BlockChain.register_node(blockchain, node)
response = {
'message': '新しいノードが追加されました',
'total_nodes': list(blockchain.nodes)
}
return jsonify(response), 201
@app.route('/nodes/resolve', methods=['GET'])
def consensus():
global blockchain
replaced, new_blockchain = resolve_conflicts(blockchain)
if replaced:
blockchain = new_blockchain
response = {
'message': 'チェーンが置き換えられました',
'new_chain': blockchain.chain
}
else:
response = {
'message': 'チェーンが確認されました',
'chain': blockchain.chain
}
return jsonify(response), 200
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment