Skip to content

Instantly share code, notes, and snippets.

@dpino
Forked from aunyks/snakecoin-server-full-code.py
Last active January 4, 2019 03:19
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dpino/07a0e90d559a959eb73fecf58a3fea92 to your computer and use it in GitHub Desktop.
Save dpino/07a0e90d559a959eb73fecf58a3fea92 to your computer and use it in GitHub Desktop.
The code in this gist isn't as succinct as I'd like it to be. Please bare with me and ask plenty of questions that you may have about it.
#!/usr/bin/env python
# Source https://gist.github.com/aunyks/47d157f8bc7d1829a729c2a6a919c173
# Modifications:
# - Can pass port number as command line argument.
# - Added GET method /add_peer.
# - On retrieving blockchain, call consensus to synchronize with other peers.
# - On updating the current blockchain from a peers' blockchain, convert list
# of JSON blocks to native Block objects.
import datetime as date
import hashlib as hasher
import json
import requests
import sys
from flask import Flask
from flask import request
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(self.attributes())
return sha.hexdigest()
def attributes(self):
return str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash)
def create_genesis_block():
return Block(0, date.datetime.now(), {
"proof-of-work": 9,
"transactions": None,
}, "0")
miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi"
blockchain = []
peer_nodes = []
mining = True
# Transaction submit.
transactions = []
@node.route('/transaction', methods=['POST'])
def transaction():
if request.method == 'POST':
transaction = request.get_json()
transactions.append(transaction)
print "New transaction"
print "FROM: {}".format(transaction['from'])
print "TO: {}".format(transaction['to'])
print "AMOUNT: {}\n".format(transaction['amount'])
return "Transaction submission successful\n"
@node.route('/blocks', methods=['GET'])
def get_blocks():
ret = []
for block in consensus():
ret.append({
"index": str(block.index),
"timestamp": str(block.timestamp),
"data": str(block.data),
"hash": block.hash,
})
return json.dumps(ret)
# Update the current blockchain to the longest blockchain across all other
# peer nodes.
def consensus():
global blockchain
longest_chain = blockchain
for chain in find_other_chains():
if len(longest_chain) < len(chain):
longest_chain = chain
return update_blockchain(longest_chain)
# Updates current blockchain. If updated is needed, converts JSON blockchain to
# list of blocks.
def update_blockchain(src):
if len(src) <= len(blockchain):
return blockchain
ret = []
for b in src:
ret.append(Block(b['index'], b['timestamp'], b['data'], b['hash']))
return ret
def find_other_chains():
ret = []
for peer in peer_nodes:
response = requests.get('http://%s/blocks' % peer)
if response.status_code == 200:
print("blocks from peer: " + response.content)
ret.append(json.loads(response.content))
return ret
@node.route('/add_peer', methods=['GET'])
def add_peer():
host = request.args['host'] if 'host' in request.args else 'localhost'
port = request.args['port']
peer = host + ':' + port
peer_nodes.append(peer)
print("Peer added: %s" % peer)
return ""
@node.route('/mine', methods = ['GET'])
def mine():
last_block = blockchain[len(blockchain) - 1]
last_proof = last_block.data['proof-of-work']
proof = proof_of_work(last_proof)
transactions.append(
{"from": "network", "to": miner_address, "amount": 1}
)
data = {
"proof-of-work": proof,
"transactions": list(transactions)
}
index = last_block.index + 1
timestamp = date.datetime.now()
# Empty transactions list.
transactions[:] = []
# Create a mined block.
block = Block(index, timestamp, data, last_block.hash)
blockchain.append(block)
return json.dumps({
"index": index,
"timestamp": str(timestamp),
"data": data,
"hash": last_block.hash
}) + "\n"
def proof_of_work(last_proof):
incrementor = last_proof + 1
while not (incrementor % 9 == 0 and incrementor % last_proof == 0):
incrementor += 1
return incrementor
def main():
port = 5000
if len(sys.argv) > 1:
port = sys.argv[1]
blockchain.append(create_genesis_block())
node.run(port=port)
if __name__ == '__main__':
main()
@makew0rld
Copy link

Thanks! How exactly are peer nodes added if the method is get? Would it be something like /add_peer?host=&port= ? Also, shouldn't the consensus algorithm also check the hashes so that false blocks can't be inserted?

@makew0rld
Copy link

As I said on the original code:
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment