[PYTHON] I tried to implement a blockchain that actually works with about 170 lines

This article is the 6th day of Blockchain Advent Calendar 2019. Yesterday was @ y-chan's Understanding atomic swap.


What happened

"There are a lot of blockchain commentary books out there, but I'm not sure how they will work in the end." "Structural blockchain seems easy, but I'm not sure how to make the contents of the block or the mining mechanism."

I was thinking "If you don't understand the explanation, you should try implementing it. It's a programmer by Mumuo." So I implemented it.

The teaching material is by Gerald Nash

--Part.1 Let ’s Build the Tiniest Blockchain (Let's make a very small blockchain) --Part.2 Let ’s Make the Tiniest Blockchain Bigger (Let's make a very small blockchain)

is.

Prerequisites

Gerald's article used as a teaching material was implemented in Python2, so I am reimplementing it in Python3 (3.5). By the way, I am fixing some parts such as "Isn't this the better processing order?" Or "I'm not calling you just to make this." In the original article, the import part of the divided code was omitted, but in this article, it will be described in the code that works [^ 1]. Also, the comments in the code are free to add if you feel the need, regardless of the original article.

All the created source code is described on the assumption that they are located in the same directory.

[^ 1]: Even in the original article, the full-size source code is published on Gist, but there were some things like "You are not using this, right?", So I am fixing it.

Implementation order

――The range of "Let's make a very small blockchain" (about 50 lines)

  1. Block implementation
  2. Check the operation of the blockchain ――The range of "Let's make a very small blockchain big" (about 130 lines)
  3. Transaction registration and new block addition

We will implement in the order of. The total number of lines excluding comment lines was about 170.

Implementation

Block implementation

First, implement the block. It is a so-called mining unit. The information contained is

--Index information

Will be.

snakecoin_block.py



import hashlib

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.hashblock()
  
  def hashblock(self):
    sha = hashlib.sha256()
    sha.update((str(self.index) +
              str(self.timestamp) +
              str(self.data) +
              str(self.previous_hash)).encode('utf-8'))
    return sha.hexdigest()

Since the previous block is required to generate a new blockchain, it is necessary to store the first block in advance. It seems that this is called Genesis block (creation block?).

For the data, the minimum value 9 calculated by the PoW (Proof of Work) algorithm described later is set as the start value. Also, since the hash of the previous block does not exist, set '0'.

snakecoin_genesis.py



from snakecoin_block import Block
import datetime

def create_genesis_block():
  return Block(0, datetime.datetime.now(), {
    'message': 'Genesis Block',
    'proof-of-work': 9
  }, '0')

In order to test up to this point, we will write a test process that adds a block to the end of the blockchain. Since it is for testing, set an appropriate character string in the data.

snakecoin_next_block.py



from snakecoin_block import Block
import datetime

def next_block(last_block):
  this_index = last_block.index + 1
  this_timestamp = datetime.datetime.now()
  this_data = "Hey! I'm block " + str(this_index)
  previous_hash = last_block.hash
  return Block(this_index, this_timestamp, this_data, previous_hash)

Blockchain operation check

Now, the blockchain as a data structure is completed. Let's check the operation by connecting the ones made so far.

The rough procedure is

  1. Create a Genesis block and create a blockchain
  2. Output block information
  3. Specified number of loops
  4. Connect a new block to the blockchain
  5. Output new block information

is.

snakecoin_blockchain_test.py



from snakecoin_genesis import create_genesis_block
from snakecoin_next_block import next_block

#Create a Genesis block to create a blockchain
blockchain = [create_genesis_block()]
#Genesis block set as end block
previous_block = blockchain[0]

#Number of blocks to connect
num_of_blocks_to_add = 3

#Output information of Genesis block
print("Block #{} has been added to the blockchain!".format(previous_block.index))
print("Data: {}".format(previous_block.data))
print("PrHh: {}".format(previous_block.previous_hash))
print("Hash: {}\n".format(previous_block.hash))

for i in range(0, num_of_blocks_to_add):
  #Create a new block and add it to the blockchain
  block_to_add = next_block(previous_block)
  blockchain.append(block_to_add)

  #Output new block information
  print("Block #{} has been added to the blockchain!".format(block_to_add.index))
  print("Data: {}".format(block_to_add.data))
  print("PrHh: {}".format(block_to_add.previous_hash))
  print("Hash: {}\n".format(block_to_add.hash))

  #Update end block
  previous_block = block_to_add

When executed, the following result will be output. The hash Hash of the own block is recorded in the next block as the hash PrHh of the previous block.

$ python snakecoin_blockchain_test.py
Block #0 has been added to the blockchain!
Data: {'proof-of-work': 9, 'message': 'Genesis Block'}
PrHh: 0
Hash: 96cab14611cd4e674d78bb2e3a93ccdf2364955575039d4ffa09a2714b12e8ac

Block #1 has been added to the blockchain!
Data: Hey! I'm block 1
PrHh: 96cab14611cd4e674d78bb2e3a93ccdf2364955575039d4ffa09a2714b12e8ac
Hash: 6023a093c0e3449692fe431679a3752a7201e74b17059087f777dfd54105f906

Block #2 has been added to the blockchain!
Data: Hey! I'm block 2
PrHh: 6023a093c0e3449692fe431679a3752a7201e74b17059087f777dfd54105f906
Hash: 18af14f5ab32bd40fa3c141290aba7a23cff058f391eb8769f4b5e4ea84aa0f8

Block #3 has been added to the blockchain!
Data: Hey! I'm block 3
PrHh: 18af14f5ab32bd40fa3c141290aba7a23cff058f391eb8769f4b5e4ea84aa0f8
Hash: 13ff0cbfcac15d705319e67abd48e3768fa6c4465ffe624689e65f29e91bf641

Transaction registration and new block addition

Now let's actually store the delivery information in the blockchain. Since REST is used as the interface, we will create it using Flask.

As an interface

--Registration of delivery transactions / transactions --Block transactions and add them to the blockchain / mines --Blockchain reference / blocks

is.

Detailed behavior and processing that may be necessary for actual operation are described in the code as comments.

snakecoin_node_transaction.py



from snakecoin_block import Block
from snakecoin_genesis import create_genesis_block
from flask import Flask, request, jsonify
import datetime
import json
import requests

#Define blockchain
blockchain = []
blockchain.append(create_genesis_block())

#Transaction list
#Transactions within this node are stored
this_nodes_tx = []

#List of node URLs on the blockchain network
# TODO:Create a mechanism to detect new nodes
peer_nodes = []

#The minor address is fixed for the time being
# TODO:Create a mechanism to uniquely generate and set each node
miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi"

#Proof of Work algorithm
#In BitCoin etc., it is a hash value search for specific conditions that requires a lot of calculation.
#Here for brevity
#"Processing * The number of times is divisible by 9" AND "Dividable by the previous result"
#* This time it's just an increment
#Is to be discovered.
#However, in this state, the server is executing the discovery process.
#Processing is not distributed, and blockchain branching is likely to occur.
# TODO:It is assumed that the part with a large amount of calculation will be implemented on the client side, and only the confirmation process will be implemented on the server side.
def proof_of_work(last_proof):
  incrementor = last_proof + 1
  while not (incrementor % 9 == 0 and incrementor % last_proof == 0):
    incrementor += 1
  return incrementor

#Get the blockchain information held by each node
def find_new_chains():
  other_chains = []
  for node_url in peer_nodes:
    block = requests.get(node_url + '/blocks').content
    block = json.reloads(block)
    other_chains.append(block)
  return other_chains

#Find the end that connects new blocks
def consensus():
  global blockchain
  longest_chain = blockchain
  #Get blockchain information held by other nodes
  other_chains = find_new_chains()
  #Search for the longest blockchain and adopt the longest blockchain.
  #If the blockchain is implemented using the current array, the information on the short branches of the branched blockchain will be lost.
  # TODO:Change to logic that adopts the longest end instead of adopting the adopted blockchain while retaining the branched branches in the implementation like a directed graph
  for chain in other_chains:
    if len(longest_chain) < len(chain):
      longest_chain = chain
  blockchain = longest_chain

#### endpoints

node = Flask(__name__)

#Register the snakecoin transfer transaction
@node.route('/transactions', methods=['POST'])
def transactions():
  if request.method == 'POST':
    #Add POSTed transaction data to transaction list
    new_tx = request.get_json()
    this_nodes_tx.append(new_tx)

    #Standard output of added transaction data
    print("New Transaction")
    print("FROM: {}".format(new_tx['from']))
    print("TO: {}".format(new_tx['to']))
    print("AMOUNT: {}".format(new_tx['amount']))

    return jsonify({'message': 'Transaction submission successful'}), 200

#Block passing transactions and connect them to the blockchain
@node.route('/mines', methods=['POST'])
def mines():
  #Get consensus
  consensus()

  #Get the last proof
  last_block = blockchain[len(blockchain) - 1]
  last_proof = last_block.data['proof-of-work']

  #Mining
  # TODO:Receive a new proof as a parameter and only perform conformity judgment
  proof = proof_of_work(last_proof)

  #Added transaction to reward miners with 1 snakecoin
  this_nodes_tx.append({
    "from": "network",
    "to": miner_address,
    "amount": 1
  })

  #Preparing the values required for the new block
  #Here the transaction list is stored in a block
  new_block_index = last_block.index + 1
  new_block_timestamp = this_timestamp = datetime.datetime.now()
  new_block_data = {
    "proof-of-work": proof,
    "transactions": list(this_nodes_tx)
  }
  last_block_hash = last_block.hash

  #Generate a new block and add it to the blockchain
  mined_block = Block(
    new_block_index,
    new_block_timestamp,
    new_block_data,
    last_block_hash
  )
  blockchain.append(mined_block)

  #Initialize transaction list
  this_nodes_tx[:] = []

  return jsonify(
    {
      "index": new_block_index,
      "timestamp": new_block_timestamp,
      "data": new_block_data,
      "hash": last_block_hash
    }
  )

#Refer to the blockchain information held by this node
@node.route('/blocks', methods=['GET'])
def get_blocks():
  chain_to_send = blockchain[:]
  for i in range(len(chain_to_send)):
    block = chain_to_send[i]
    #String the properties of the Block class
    block_index = str(block.index)
    block_timestamp = str(block.timestamp)
    block_data = str(block.data)
    block_hash = block.hash
    #Convert to dictionary type so that it can be converted to JSON string
    chain_to_send[i] = {
      "index": block_index,
      "timestamp": block_timestamp,
      "data": block_data,
      "hash": block_hash
    }
  #Convert to JSON string and return to client
  return jsonify(chain_to_send)

node.run()

In the original article, cURL was used to check the operation, so in this article, I will post the execution result of Postman.

$ python snakecoin_node_transaction.py

When you start with and GET / blocks, the following contents will be returned.

image.png

A blockchain (array) containing Genesis blocks has been returned. Next, let's register the delivery information of snakecoin with POST / transactions.

image.png

2 The transaction that passed the snakecoins was successful (Status: 200 OK). However, this transaction has not yet been recorded on the blockchain (= not completed). You can see that the contents have not changed even if you hit GET / blocks.

image.png

Let's mine to complete the delivery transaction. Hit POST / mines.

image.png

The newly created block information is returned. In addition to the delivery information of 2 snakecoins, the delivery information of 1 snakecoin is included as a reward to the miner (minor).

image.png

If you check GET / blocks, you can see that new blocks have been added to the blockchain.

Summary and impressions

Blockchain is not scary!

I still want to know about Etherium's PoS (Proof of Stake), not PoW, such as P2P network construction or wallet implementation! I feel that there are various shortages, but I feel that I have reached the point where I fully understand the blockchain ().

There are still some things that need to be investigated before actually operating it, but from the point where "I can't see the way to actual operation" to "I think I can make a point system for fun in a little more". think. But after all, does the implementation around mining give the client a server function as well? I'm still not sure about it, so I thought I'd study by reading Advent Calendar Article.

I hope this helps you understand the blockchain. And thanks again to Mr. Gerald.


tomorrow? Is @ shu-kob's Let's touch Bitcoin Signet.

Recommended Posts

I tried to implement a blockchain that actually works with about 170 lines
Implement blockchain with about 60 lines
I tried to implement a volume moving average with Quantx
I tried to implement Autoencoder with TensorFlow
I tried to implement CVAE with PyTorch
[Python] A memo that I tried to get started with asyncio
I tried to implement reading Dataset with PyTorch
I tried to implement merge sort in Python with as few lines as possible
When I tried to connect with SSH, I got a warning about free space.
I tried to make a translation BOT that works on Discord using googletrans
I tried to create a table only with Django
I tried to implement and learn DCGAN with PyTorch
I tried to implement Minesweeper on terminal with python
I tried to draw a route map with Python
I tried to implement a pseudo pachislot in Python
I tried to implement a recommendation system (content-based filtering)
I tried to implement an artificial perceptron with python
I tried to implement time series prediction with GBDT
I tried to automatically generate a password with Python3
I tried to implement Grad-CAM with keras and tensorflow
I tried to implement SSD with PyTorch now (Dataset)
I tried to implement PCANet
I tried to implement StarGAN (1)
I tried to implement a basic Recurrent Neural Network model
I tried to implement a one-dimensional cellular automaton in Python
I tried to automatically create a report with Markov chain
I tried to implement breakout (deception avoidance type) with Quantx
I tried to solve a combination optimization problem with Qiskit
I tried to get started with Hy ・ Define a class
[Python] I tried to visualize tweets about Corona with WordCloud
I tried to implement ListNet of rank learning with Chainer
I made a rigid Pomodoro timer that works with CUI
I tried to implement Harry Potter sort hat with CNN
I tried to sort a random FizzBuzz column with bubble sort.
I tried to divide with a deep learning language model
I tried to implement SSD with PyTorch now (model edition)
I tried to implement Deep VQE
[1 hour challenge] I tried to make a fortune-telling site that is too suitable with Python
I tried to implement adversarial validation
I tried to make a generator that generates a C # container class from CSV with Python
I tried to implement hierarchical clustering
I tried to organize about MCMC.
I tried to implement Realness GAN
I tried to build a service that sells machine-learned data at explosive speed with Docker
[5th] I tried to make a certain authenticator-like tool with python
I tried to create a server environment that runs on Windows 10
I want to use a wildcard that I want to shell with Python remove
I tried to make a system that fetches only deleted tweets
[2nd] I tried to make a certain authenticator-like tool with python
A memorandum when I tried to get it automatically with selenium
[Python] I tried to implement stable sorting, so make a note
I tried to implement anomaly detection using a hidden Markov model
[3rd] I tried to make a certain authenticator-like tool with python
I tried to create a list of prime numbers with python
I tried to implement a misunderstood prisoner's dilemma game in Python
I tried to make a periodical process with Selenium and Python
I tried to make a 2channel post notification application with Python
I tried to implement sentence classification by Self Attention with PyTorch
I tried to create Bulls and Cows with a shell program
I tried a tool that imitates Van Gogh's pattern with AI
I tried to make a todo application using bottle with python