Recently, I watched the StartUp series on Netflix. So, I want to make my own blockchain to conquer/save/change the world. However, I am not good at math and in cryptography, so I will just present you the simplest blockchain (distributed database would be more correct) in python I could do.
First, let’s find a simple and funny name: FunCoin!
We will use socket to communicate between nodes of our network. All the nodes will save the same data, then you can access data from any node.
The code
import socket
import sys
from _thread import *
import threadingimport uuidprint_lock = threading.Lock()port = 5005#network port no above 1024
currentNodeName = "myMainNode"
nodes = [] #(name, address)
blocks = [] #(address, data)
users = [] #(id, password)
amount = {} #(id:amount)
transactions = [] #(id)def sendCommandTo(address, command):
global port
print("sendCommandTo", address, command)
try:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((address, port))
client_socket.send(command.encode())
client_socket.close()
except:
print("Error")def shareCommand(command):
global nodes, currentNodeName, host_ip
for nodeName, nodeAddress in nodes:
if nodeName == currentNodeName and nodeAddress == host_ip:#Current node
continue
sendCommandTo(nodeAddress, command)def getMyIp():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]host_ip = getMyIp()def commandExecution(conn, address):
global port, currentNodeName, host_ip, nodes, blocks, users, amount, transactionsclientAddress, clientPort = address
print("Connection from: " + str(address))
while True:
# receive data stream. it won't accept data packet greater than 1024 bytes
data = conn.recv(1024).decode()
if not data:
print_lock.release()
break
inputData = str(data)
print("Input data: " + inputData)
response = "You sent: " + inputData
conn.send(response.encode())# send data to the client
command = inputData.replace("\r", "").replace("\n", "").split(' ')
print("Command", command)#For user
if len(command) >= 2 and command[0] == 'userNew':
joiningPassword = command[1]
newUserId = str(uuid.uuid4())#can be duplicate
users.append((newUserId, joiningPassword))
amount[newUserId] = 10 #give 10 coin to new user
conn.send((newUserId + "\n").encode())
shareCommand("nodeNewUser " + newUserId + " " + joiningPassword)
elif len(command) >= 4 and command[0] == 'addData':
userId = command[1]
userPassword = command[2]
userData = command[3]
if (userId, userPassword) not in users:
conn.send(("ID or password are not correct" + "\n").encode())
continue
if (userId, userData) in blocks:
conn.send(("Existing data" + "\n").encode())
continue
blocks.append((userId, userData))
shareCommand(inputData)
elif len(command) >= 1 and command[0] == 'showData':
conn.send((str(blocks) + "\n").encode())
elif len(command) >= 2 and command[0] == 'amount':
userId = command[1]
if userId in amount:
conn.send((str(amount[userId]) + "\n").encode())
else:
conn.send((str(0 + "\n").encode()))
elif len(command) >= 5 and command[0] == 'sendTo':
userId = command[1]
userPassword = command[2]
userTarget = command[3]
transferAmount = float(command[4])
if (userId, userPassword) not in users:
conn.send(("ID or password are not correct" + "\n").encode())
continue
userAmount = amount[userId]
if userAmount < transferAmount:
conn.send(("Not enough coin" + "\n").encode())
continue
if userTarget not in amount:
conn.send(("Unknow target" + "\n").encode())
continue
transactionsId = str(uuid.uuid4())
transactions.append(transactionsId)
amount[userId]-=transferAmount
amount[userTarget]+=transferAmount
shareCommand("nodeSendTo" + " " + userId + " " + userPassword + " " + userTarget + " " + str(transferAmount) + " " + transactionsId)elif len(command) >= 1 and command[0] == 'nodeList':
conn.send((str(nodes) + "\n").encode())#For node
elif len(command) >= 2 and command[0] == 'join':
joiningNodeName = command[1]if (joiningNodeName, clientAddress) in nodes:
continue#Already in list
nodes.append((joiningNodeName, clientAddress))shareCommand(inputData)
elif len(command) >= 3 and command[0] == 'nodeNewUser':
newUserId = command[1]
joiningPassword = command[2]if (newUserId, joiningPassword) in users:
continueusers.append((newUserId, joiningPassword))
amount[newUserId] = 10 #give 10 coin to new user
shareCommand("nodeNewUser " + newUserId + " " + joiningPassword)
elif len(command) >= 6 and command[0] == 'nodeSendTo':
userId = command[1]
userPassword = command[2]
userTarget = command[3]
transferAmount = float(command[4])
transactionsId = command[5]
if (userId, userPassword) not in users:
conn.send(("ID or password are not correct" + "\n").encode())
continue
userAmount = amount[userId]
if userAmount < transferAmount:
conn.send(("Not enough coin" + "\n").encode())
continue
if userTarget not in amount:
conn.send(("Unknow target" + "\n").encode())
continue
if transactionsId in transactions:
continue
transactions.append(transactionsId)
amount[userId]-=transferAmount
amount[userTarget]+=transferAmount
shareCommand("nodeSendTo" + " " + userId + " " + userPassword + " " + userTarget + " " + str(transferAmount) + " " + transactionsId)conn.close()# close the connectiondef server_program():
global port, currentNodeName, host_ip
host = socket.gethostname()server_socket = socket.socket() # get instance
# look closely. The bind() function takes tuple as argument
server_socket.bind(("0.0.0.0", port)) # bind host address and port together
print("Node name", host, host_ip, port)
nodes.append((currentNodeName, host_ip))# configure how many client the server can listen simultaneously
server_socket.listen(2)
while True:
conn, address = server_socket.accept() # accept new connection
print_lock.acquire()
start_new_thread(commandExecution, (conn, address,))
server_socket.close()from http.server import BaseHTTPRequestHandler, HTTPServerhostName = "localhost"
serverPort = 8080class MyServer(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes("<html><head><title>FunCoin</title></head>", "utf-8"))
self.wfile.write(bytes("<body>", "utf-8"))
self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
self.wfile.write(bytes("<p>Nodes: %s</p>" % nodes, "utf-8"))
self.wfile.write(bytes("<p>Data: %s</p>" % blocks, "utf-8"))
self.wfile.write(bytes("<p>Users: %s</p>" % users, "utf-8"))
self.wfile.write(bytes("<p>Amount: %s</p>" % amount, "utf-8"))
self.wfile.write(bytes("</body></html>", "utf-8"))def startWebServer():
webServer = HTTPServer((hostName, serverPort), MyServer)
print("Server started http://%s:%s" % (hostName, serverPort))
webServer.serve_forever()
webServer.server_close()
print("Server stopped.")if __name__ == '__main__':
if len(sys.argv) >=2:
nodeToJoin = sys.argv[1]
print("Join node", nodeToJoin)
sendCommandTo(nodeToJoin, "join " + currentNodeName)start_new_thread(startWebServer, ())
server_program()
How to use
Join a network:
python node.py [address of a node to sync with]
If you want to participate in an external network, you will need a public IP address.
Create a new network:
python node.py
Commands
Use telnet [node address] 5005 to send command to a node.
- userNew [password] → register a new user; return userId
- addData userId password data → save data to the blockchain
- showData → return data contain in the node
- amount userId → return coin amount of the user
- sendTo userId password targetId amount → send coin to another user
Check state trough browser
Hardware
Possible use case
Since data stored by nodes can be access trough simple telnet client or browser, you can save your CV in the blockchain to look cool when you search a job.
- Node earn coin by saving and giving access to data
- User use coin to save their data
What we still have to do
- Synchronize data with new node
- Implement a proof method, for example check several nodes before transfer coin
Most important part of this article!
My Bitcoin address is: 3LnWq2kMdjE5SX8DK8zsB9zPKm2qHuCVm6
My Ethereum address is: 0xa24ac8c8daee4d27c5d2cddd066bfca2622ef2fe
Feel free to support me, I will improve the article for any transfer ;)
In the next article, we will build our own darknet.