This article is the 6th day of Blockchain Advent Calendar 2019. Yesterday was @ y-chan's Understanding atomic swap.
"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.
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.
――The range of "Let's make a very small blockchain" (about 50 lines)
We will implement in the order of. The total number of lines excluding comment lines was about 170.
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)
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
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
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.
A blockchain (array) containing Genesis blocks has been returned. Next, let's register the delivery information of snakecoin with POST / transactions
.
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
.
Let's mine to complete the delivery transaction. Hit POST / mines
.
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).
If you check GET / blocks
, you can see that new blocks have been added to the blockchain.
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