-
-
Save aunyks/47d157f8bc7d1829a729c2a6a919c173 to your computer and use it in GitHub Desktop.
from flask import Flask | |
from flask import request | |
import json | |
import requests | |
import hashlib as hasher | |
import datetime as date | |
node = Flask(__name__) | |
# Define what a Snakecoin block is | |
class Block: | |
def __init__(self, index, timestamp, data, previous_hash): | |
self.index = index | |
self.timestamp = timestamp | |
self.data = data | |
self.previous_hash = previous_hash | |
self.hash = self.hash_block() | |
def hash_block(self): | |
sha = hasher.sha256() | |
sha.update(str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash)) | |
return sha.hexdigest() | |
# Generate genesis block | |
def create_genesis_block(): | |
# Manually construct a block with | |
# index zero and arbitrary previous hash | |
return Block(0, date.datetime.now(), { | |
"proof-of-work": 9, | |
"transactions": None | |
}, "0") | |
# A completely random address of the owner of this node | |
miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi" | |
# This node's blockchain copy | |
blockchain = [] | |
blockchain.append(create_genesis_block()) | |
# Store the transactions that | |
# this node has in a list | |
this_nodes_transactions = [] | |
# Store the url data of every | |
# other node in the network | |
# so that we can communicate | |
# with them | |
peer_nodes = [] | |
# A variable to deciding if we're mining or not | |
mining = True | |
@node.route('/txion', methods=['POST']) | |
def transaction(): | |
# On each new POST request, | |
# we extract the transaction data | |
new_txion = request.get_json() | |
# Then we add the transaction to our list | |
this_nodes_transactions.append(new_txion) | |
# Because the transaction was successfully | |
# submitted, we log it to our console | |
print "New transaction" | |
print "FROM: {}".format(new_txion['from'].encode('ascii','replace')) | |
print "TO: {}".format(new_txion['to'].encode('ascii','replace')) | |
print "AMOUNT: {}\n".format(new_txion['amount']) | |
# Then we let the client know it worked out | |
return "Transaction submission successful\n" | |
@node.route('/blocks', methods=['GET']) | |
def get_blocks(): | |
chain_to_send = blockchain | |
# Convert our blocks into dictionaries | |
# so we can send them as json objects later | |
for i in range(len(chain_to_send)): | |
block = chain_to_send[i] | |
block_index = str(block.index) | |
block_timestamp = str(block.timestamp) | |
block_data = str(block.data) | |
block_hash = block.hash | |
chain_to_send[i] = { | |
"index": block_index, | |
"timestamp": block_timestamp, | |
"data": block_data, | |
"hash": block_hash | |
} | |
chain_to_send = json.dumps(chain_to_send) | |
return chain_to_send | |
def find_new_chains(): | |
# Get the blockchains of every | |
# other node | |
other_chains = [] | |
for node_url in peer_nodes: | |
# Get their chains using a GET request | |
block = requests.get(node_url + "/blocks").content | |
# Convert the JSON object to a Python dictionary | |
block = json.loads(block) | |
# Add it to our list | |
other_chains.append(block) | |
return other_chains | |
def consensus(): | |
# Get the blocks from other nodes | |
other_chains = find_new_chains() | |
# If our chain isn't longest, | |
# then we store the longest chain | |
longest_chain = blockchain | |
for chain in other_chains: | |
if len(longest_chain) < len(chain): | |
longest_chain = chain | |
# If the longest chain isn't ours, | |
# then we stop mining and set | |
# our chain to the longest one | |
blockchain = longest_chain | |
def proof_of_work(last_proof): | |
# Create a variable that we will use to find | |
# our next proof of work | |
incrementor = last_proof + 1 | |
# Keep incrementing the incrementor until | |
# it's equal to a number divisible by 9 | |
# and the proof of work of the previous | |
# block in the chain | |
while not (incrementor % 9 == 0 and incrementor % last_proof == 0): | |
incrementor += 1 | |
# Once that number is found, | |
# we can return it as a proof | |
# of our work | |
return incrementor | |
@node.route('/mine', methods = ['GET']) | |
def mine(): | |
# Get the last proof of work | |
last_block = blockchain[len(blockchain) - 1] | |
last_proof = last_block.data['proof-of-work'] | |
# Find the proof of work for | |
# the current block being mined | |
# Note: The program will hang here until a new | |
# proof of work is found | |
proof = proof_of_work(last_proof) | |
# Once we find a valid proof of work, | |
# we know we can mine a block so | |
# we reward the miner by adding a transaction | |
this_nodes_transactions.append( | |
{ "from": "network", "to": miner_address, "amount": 1 } | |
) | |
# Now we can gather the data needed | |
# to create the new block | |
new_block_data = { | |
"proof-of-work": proof, | |
"transactions": list(this_nodes_transactions) | |
} | |
new_block_index = last_block.index + 1 | |
new_block_timestamp = this_timestamp = date.datetime.now() | |
last_block_hash = last_block.hash | |
# Empty transaction list | |
this_nodes_transactions[:] = [] | |
# Now create the | |
# new block! | |
mined_block = Block( | |
new_block_index, | |
new_block_timestamp, | |
new_block_data, | |
last_block_hash | |
) | |
blockchain.append(mined_block) | |
# Let the client know we mined a block | |
return json.dumps({ | |
"index": new_block_index, | |
"timestamp": str(new_block_timestamp), | |
"data": new_block_data, | |
"hash": last_block_hash | |
}) + "\n" | |
node.run() | |
@DarkNTNT84 thanks, I will actually not have to implement mining, which is why I was looking for the next block function. Thank you!
This code doesn't broadcast transactions to all nodes as mentioned in the bitcoin whitepaper, so each block would only contain the transactions for the user and miner of that node, instead of combining transactions from multiple nodes. /transaction should have a GET method which would supply other nodes will all the transactions this node has, which would then be added to this_node_transactions.
To fix Internal Server Error aka "dict" object has no attribute "index", simply change chain_to_send = blockchain
to chain_to_send = blockchain[:]
. This will copy the blockchain array. So you wouldn't change the actual blockchain to JSON.
I keep getting this error when I'm using /txion
Ms-MacBook-Pro:mvb m$ curl localhost:5000/transaction
<title>405 Method Not Allowed</title>Method Not Allowed
The method is not allowed for the requested URL.
What am I doing wrong ?
I have tried doing what DarkNTNT84 mentions, but it still won't work
@Cole128,
When you run the command "curl localhost:5000/mine", the new block will be added. In this code, the function "mine" in charge the adding new block.