[PYTHON] J'ai essayé de mettre en œuvre une blockchain qui fonctionne réellement avec environ 170 lignes

Cet article est le 6ème jour du Calendrier de l'Avent Blockchain 2019. Hier, c'était [Comprendre l'échange atomique] de @ y-chan (https://qiita.com/y-chan/items/f69b628f65ce12097e4a).


Qu'est-il arrivé

«Il existe de nombreux livres de commentaires sur la blockchain, mais je ne sais pas comment ils fonctionneront à la fin. "La blockchain structurelle semble facile, mais je ne sais pas comment créer le contenu du bloc ou comment le minage fonctionne."

je pensais "Si vous ne comprenez pas l'explication, vous devriez essayer de l'implémenter. C'est un programmeur de Mumuo." Alors je l'ai implémenté.

Le matériel pédagogique est de Gerald Nash

--Part.1 Construisons la plus petite blockchain (Faisons une très petite blockchain) --Part.2 Rendons la plus petite blockchain plus grande (Faisons une très petite blockchain)

est.

Conditions préalables

L'article de Gerald utilisé comme matériel pédagogique a été implémenté en Python2, il a donc été réimplémenté en Python3 (3.5). En passant, je corrige certaines parties telles que "N'est-ce pas le meilleur ordre de traitement?" Ou "Je ne vous appelle pas juste pour faire ça". Dans l'article d'origine, la partie import du code divisé a été omise, mais dans cet article, elle sera décrite dans le code qui fonctionne [^ 1]. De plus, les commentaires dans le code ne sont pas limités à l'article original et peuvent être ajoutés librement si nécessaire.

Tout le code source créé est décrit en supposant qu'il se trouve dans le même répertoire.

[^ 1]: Même dans l'article original, le code source en taille réelle est publié sur Gist, mais il y avait des choses comme "Vous ne l'utilisez pas, n'est-ce pas?", Donc je suis en train de le réparer.

Ordre d'exécution

――La gamme de "Faisons une toute petite blockchain" (environ 50 lignes)

  1. Bloquer la mise en œuvre
  2. Vérifiez le fonctionnement de la blockchain ――La gamme de "Faisons une très petite blockchain grande" (environ 130 lignes)
  3. Enregistrez la transaction et ajoutez un nouveau bloc

Nous mettrons en œuvre dans l'ordre de. Le nombre total de lignes à l'exclusion des lignes de commentaires était d'environ 170.

Il est temps de mettre en œuvre

Bloquer l'implémentation

Tout d'abord, implémentez le bloc. C'est une soi-disant unité minière. Les informations contenues sont

--Informations d'index

Sera.

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()

Étant donné que le bloc précédent est nécessaire pour générer une nouvelle blockchain, le premier bloc doit être stocké à l'avance. Il semble que cela s'appelle le bloc Genesis (bloc de création?).

Pour les données, la valeur minimale «9» calculée par l'algorithme PoW (Proof of Work) décrit plus loin est définie comme valeur de départ. De plus, puisque le hachage du bloc précédent n'existe pas, définissez `` 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')

Afin de tester jusqu'à ce point, nous allons écrire un processus de test qui ajoute un bloc à la fin de la blockchain. Puisqu'il s'agit de tests, définissez une chaîne de caractères appropriée dans les données.

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)

Vérification du fonctionnement de la blockchain

Maintenant, la blockchain en tant que structure de données est terminée. Vérifions le fonctionnement en connectant ceux réalisés jusqu'à présent.

La procédure approximative est

  1. Créez un bloc Genesis et créez une blockchain
  2. Informations sur le bloc de sortie
  3. Nombre spécifié de boucles
  4. Connectez un nouveau bloc à la blockchain
  5. Sortie des nouvelles informations de bloc

est.

snakecoin_blockchain_test.py



from snakecoin_genesis import create_genesis_block
from snakecoin_next_block import next_block

#Créez un bloc Genesis pour créer une blockchain
blockchain = [create_genesis_block()]
#Bloc Genesis défini comme bloc d'extrémité
previous_block = blockchain[0]

#Nombre de blocs à connecter
num_of_blocks_to_add = 3

#Informations de sortie du bloc Genesis
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):
  #Créez un nouveau bloc et ajoutez-le à la blockchain
  block_to_add = next_block(previous_block)
  blockchain.append(block_to_add)

  #Sortie de nouvelles informations de bloc
  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))

  #Mettre à jour le bloc d'extrémité
  previous_block = block_to_add

Une fois exécuté, le résultat suivant sera affiché. Le hachage «Hash» du propre bloc est enregistré dans le bloc suivant en tant que hachage «PrHh» du bloc précédent.

$ 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

Enregistrement de transaction et ajout d'un nouveau bloc

Maintenant, stockons réellement les informations de livraison dans la blockchain. Puisque REST est utilisé comme interface, nous le créerons en utilisant Flask.

Comme interface

--Enregistrement des transactions de livraison / transactions --Bloquez les transactions et ajoutez-les à la blockchain / mines --Blockchain référence / blocks

est.

Le comportement détaillé et le traitement qui peuvent être nécessaires pour le fonctionnement réel sont décrits dans le code sous forme de commentaires.

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

#Définir la blockchain
blockchain = []
blockchain.append(create_genesis_block())

#Liste des transactions
#Stocke les transactions dans ce nœud
this_nodes_tx = []

#Liste des URL de nœuds sur le réseau blockchain
# TODO:Créer un mécanisme pour détecter les nouveaux nœuds
peer_nodes = []

#L'adresse mineure est fixe pour le moment
# TODO:Créer un mécanisme pour générer et définir de manière unique chaque nœud
miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi"

#Algorithme de preuve de travail
#Dans BitCoin, etc., il s'agit d'une recherche de valeur de hachage pour des conditions spécifiques qui nécessite beaucoup de calcul.
#Ici pour la brièveté
#"Traitement * Le nombre de fois est divisible par 9" ET "Divisible par le résultat précédent"
#* Cette fois, c'est juste un incrément
#Est à découvrir.
#Cependant, dans cet état, le serveur exécute le processus de découverte.
#Le traitement n'est pas distribué et un branchement de la blockchain est susceptible de se produire.
# TODO:On suppose que la partie avec une grande quantité de calcul sera implémentée côté client, et seul le processus de confirmation sera implémenté côté serveur.
def proof_of_work(last_proof):
  incrementor = last_proof + 1
  while not (incrementor % 9 == 0 and incrementor % last_proof == 0):
    incrementor += 1
  return incrementor

#Obtenez les informations de la blockchain détenues par chaque nœud
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

#Trouvez l'extrémité qui relie de nouveaux blocs
def consensus():
  global blockchain
  longest_chain = blockchain
  #Obtenez des informations sur la blockchain détenues par d'autres nœuds
  other_chains = find_new_chains()
  #Recherchez la blockchain la plus longue et adoptez la plus longue blockchain.
  #Si la blockchain est mise en œuvre en utilisant l'arrangement actuel, les informations sur les branches courtes de la blockchain branchée seront perdues.
  # TODO:Changement de logique qui adopte l'extrémité la plus longue au lieu d'adopter la blockchain adoptée tout en conservant les branches branchées dans l'implémentation comme un graphe orienté
  for chain in other_chains:
    if len(longest_chain) < len(chain):
      longest_chain = chain
  blockchain = longest_chain

#### endpoints

node = Flask(__name__)

#Enregistrer une transaction de pass snakecoin
@node.route('/transactions', methods=['POST'])
def transactions():
  if request.method == 'POST':
    #Ajouter les données de transaction POSTÉES à la liste des transactions
    new_tx = request.get_json()
    this_nodes_tx.append(new_tx)

    #Sortie standard des données de transaction ajoutées
    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

#Bloquer les transactions de passage et les connecter à la blockchain
@node.route('/mines', methods=['POST'])
def mines():
  #Prendre le consensus
  consensus()

  #Obtenez la dernière preuve
  last_block = blockchain[len(blockchain) - 1]
  last_proof = last_block.data['proof-of-work']

  #Exploitation minière
  # TODO:Recevoir une nouvelle preuve en tant que paramètre et effectuer uniquement un jugement de conformité
  proof = proof_of_work(last_proof)

  #Ajout d'une transaction qui récompense les mineurs avec 1 snakecoin
  this_nodes_tx.append({
    "from": "network",
    "to": miner_address,
    "amount": 1
  })

  #Préparation des valeurs requises pour le nouveau bloc
  #Ici, la liste des transactions est stockée dans un bloc
  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

  #Générez un nouveau bloc et ajoutez-le à la blockchain
  mined_block = Block(
    new_block_index,
    new_block_timestamp,
    new_block_data,
    last_block_hash
  )
  blockchain.append(mined_block)

  #Initialiser la liste des transactions
  this_nodes_tx[:] = []

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

#Reportez-vous aux informations de blockchain détenues par ce nœud
@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]
    #Chaîne les propriétés de la classe Block
    block_index = str(block.index)
    block_timestamp = str(block.timestamp)
    block_data = str(block.data)
    block_hash = block.hash
    #Convertir en type de dictionnaire afin qu'il puisse être converti en chaîne JSON
    chain_to_send[i] = {
      "index": block_index,
      "timestamp": block_timestamp,
      "data": block_data,
      "hash": block_hash
    }
  #Convertir en chaîne JSON et retourner au client
  return jsonify(chain_to_send)

node.run()

Dans l'article d'origine, cURL a été utilisé pour vérifier l'opération, donc dans cet article, je publierai le résultat de l'exécution de Postman.

$ python snakecoin_node_transaction.py

Lorsque vous démarrez avec et GET / blocks, le contenu suivant sera renvoyé.

image.png

Une blockchain (arrangement) contenant des blocs Genesis a été renvoyée. Ensuite, enregistrons les informations de livraison de snakecoin avec POST / transactions.

image.png

2 La transaction qui a passé les snakecoins a réussi (Statut: 200 OK). Cependant, cette transaction n'a pas encore été enregistrée dans la blockchain (= non terminée). Vous pouvez voir que le contenu n'a pas changé même si vous cliquez sur GET / blocks.

image.png

Faisons du minage pour terminer la transaction de livraison. Appuyez sur «POST / mines».

image.png

Les informations de bloc nouvellement créées sont renvoyées. En plus des informations de livraison de 2 snakecoins, les informations de livraison de 1 snakecoin sont incluses en récompense du mineur (mineur).

image.png

Si vous cochez GET / blocks, vous pouvez voir que de nouveaux blocs ont été ajoutés à la blockchain.

Résumé et impressions

La blockchain ne fait pas peur!

Je veux toujours en savoir plus sur le PoS (Proof of Stake) d'Etherium, pas sur le PoW, comme la construction d'un réseau P2P ou la mise en œuvre de Wallet! Je sens qu'il y a diverses pénuries, mais je sens que j'ai atteint le point où je comprends parfaitement la blockchain ().

Il y a encore des choses qui doivent être étudiées avant de l'utiliser, mais du point où «je ne vois pas le chemin du fonctionnement réel» à «je pense que je peux faire un système de points pour m'amuser un peu plus». pense. Mais après tout, la mise en œuvre autour de l'exploitation minière donne-t-elle également au client une fonction de serveur? Je n'en suis toujours pas sûr, alors j'ai pensé étudier en lisant Article du calendrier de l'Avent.

J'espère que cela vous aidera à comprendre la blockchain. Et merci encore à M. Gerald.


demain? Est-ce que @ shu-kob Let's touch Bitcoin Signet.

Recommended Posts

J'ai essayé de mettre en œuvre une blockchain qui fonctionne réellement avec environ 170 lignes
Implémentez la blockchain avec environ 60 lignes
J'ai essayé d'implémenter une ligne moyenne mobile de volume avec Quantx
J'ai essayé d'implémenter Autoencoder avec TensorFlow
J'ai essayé d'implémenter CVAE avec PyTorch
[Python] Un mémo que j'ai essayé de démarrer avec asyncio
J'ai essayé d'implémenter la lecture de Dataset avec PyTorch
J'ai essayé d'implémenter le tri par fusion en Python avec le moins de lignes possible
J'ai essayé de créer un BOT de traduction qui fonctionne avec Discord en utilisant googletrans
J'ai essayé d'implémenter et d'apprendre DCGAN avec PyTorch
J'ai essayé d'implémenter Mine Sweeper sur un terminal avec python
J'ai essayé d'implémenter un pseudo pachislot en Python
J'ai essayé d'implémenter le perceptron artificiel avec python
J'ai essayé d'implémenter Grad-CAM avec keras et tensorflow
J'ai essayé d'implémenter SSD avec PyTorch maintenant (Dataset)
J'ai essayé d'implémenter PCANet
J'ai essayé d'implémenter StarGAN (1)
J'ai essayé de mettre en œuvre le modèle de base du réseau neuronal récurrent
J'ai essayé d'implémenter un automate cellulaire unidimensionnel en Python
J'ai essayé de créer automatiquement un rapport avec la chaîne de Markov
J'ai essayé de mettre en œuvre une évasion (type d'évitement de tromperie) avec Quantx
J'ai essayé de résoudre le problème d'optimisation des combinaisons avec Qiskit
J'ai essayé de commencer avec Hy ・ Définir une classe
[Python] J'ai essayé de visualiser des tweets sur Corona avec WordCloud
J'ai essayé d'implémenter ListNet d'apprentissage de rang avec Chainer
J'ai fait une minuterie pomodoro dure qui fonctionne avec CUI
J'ai essayé de mettre en œuvre le chapeau de regroupement de Harry Potter avec CNN
J'ai essayé de trier une colonne FizzBuzz aléatoire avec un tri à bulles.
J'ai essayé d'écrire dans un modèle de langage profondément appris
J'ai essayé d'implémenter SSD avec PyTorch maintenant (édition du modèle)
J'ai essayé d'implémenter Deep VQE
[1 hour challenge] J'ai essayé de créer un site de bonne aventure qui soit trop adapté à Python
J'ai essayé de mettre en place une validation contradictoire
J'ai essayé de créer un générateur qui génère une classe conteneur C # à partir de CSV avec Python
J'ai essayé de m'organiser à propos de MCMC.
J'ai essayé d'implémenter Realness GAN
J'ai essayé de créer un service qui vend des données apprises par machine à une vitesse explosive avec Docker
[5e] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de créer un environnement serveur qui fonctionne sous Windows 10
Je souhaite utiliser un caractère générique que je souhaite décortiquer avec Python remove
J'ai essayé de créer un système qui ne récupère que les tweets supprimés
[2nd] J'ai essayé de créer un certain outil de type Authenticator avec python
Un mémorandum lors de l'acquisition automatique avec du sélénium
[Python] J'ai essayé d'implémenter un tri stable, alors notez
[3ème] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de créer une liste de nombres premiers avec python
J'ai essayé de mettre en œuvre un jeu de dilemme de prisonnier mal compris en Python
J'ai essayé de faire un processus d'exécution périodique avec Selenium et Python
J'ai essayé de créer une application de notification de publication à 2 canaux avec Python
J'ai essayé d'implémenter la classification des phrases par Self Attention avec PyTorch
J'ai essayé de créer des taureaux et des vaches avec un programme shell
J'ai essayé un outil qui imite le modèle de Goch avec l'IA
J'ai essayé de créer une application todo en utilisant une bouteille avec python