Skip to content

Instantly share code, notes, and snippets.

@aunyks
Last active March 8, 2024 19:22
Show Gist options
  • Save aunyks/47d157f8bc7d1829a729c2a6a919c173 to your computer and use it in GitHub Desktop.
Save aunyks/47d157f8bc7d1829a729c2a6a919c173 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.
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()
@nroble
Copy link

nroble commented Feb 8, 2018

@djalmabright try sha.update(( str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash)).encode('utf-8') )

@spicyramen
Copy link

spicyramen commented Feb 17, 2018

def hash_block(self):
    """

    :return:
    """
    sha = hasher.sha256()
    sha.update(self.attributes())
    return sha.hexdigest()

def attributes(self):
    return '%s%s%s%s' % (self.index, self.timestamp, self.data, self.previous_hash)

@hamidabuisa
Copy link

Any thoughts on this error?

C:\Users\HI>curl "localhost:5000/txion" \

<title>405 Method Not Allowed</title>

Method Not Allowed

The method is not allowed for the requested URL.

curl: (6) Could not resolve host: \

@DarkNTNT84
Copy link

DarkNTNT84 commented Feb 23, 2018

@paulvijaykumarm, @hamidabuisa
Hi,

When I used the command : curl "localhost:5000/txion" -H "Contect-Type: application/json" -d '{"from": "akjflw", "to":"fjlakdj", "amount": 3}'
The error appeared.

I changed the command and it worked:
image

image
image

@makew0rld
Copy link

makew0rld commented Feb 24, 2018

Where is the next_block function mentioned in part 1? is it replaced with mining?

@DarkNTNT84
Copy link

DarkNTNT84 commented Feb 24, 2018

@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.

@makew0rld
Copy link

@DarkNTNT84 thanks, I will actually not have to implement mining, which is why I was looking for the next block function. Thank you!

@makew0rld
Copy link

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.

@moliata
Copy link

moliata commented Jun 1, 2018

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.

@mgherghi
Copy link

mgherghi commented Jun 3, 2019

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

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