Python Blockchain - Guía rápida

En el tutorial sobre Blockchain, hemos aprendido en detalle sobre la teoría detrás de blockchain. La cadena de bloques es el bloque de construcción fundamental detrás de la moneda digital Bitcoin más popular del mundo. El tutorial abordó en profundidad las complejidades de Bitcoin explicando completamente la arquitectura blockchain. El siguiente paso es construir nuestra propia cadena de bloques.

Satoshi Nakamoto creó la primera moneda virtual del mundo llamada Bitcoin. Al observar el éxito de Bitcoin, muchos otros crearon sus propias monedas virtuales. Por nombrar algunos: Litecoin, Zcash, etc.

Ahora, es posible que también desee lanzar su propia moneda. Llamemos a esto como TPCoin (TutorialsPoint Coin). Escribirás una cadena de bloques para registrar todas las transacciones relacionadas con TPCoin. El TPCoin se puede utilizar para comprar pizzas, hamburguesas, ensaladas, etc. Es posible que haya otros proveedores de servicios que se unan a su red y comiencen a aceptar TPCoin como la moneda para brindar sus servicios. Las posibilidades son infinitas.

En este tutorial, entendamos cómo construir un sistema de este tipo y lanzar su propia moneda digital en el mercado.

Componentes involucrados en el desarrollo de proyectos Blockchain

Todo el desarrollo del proyecto blockchain consta de tres componentes principales:

  • Client
  • Miners
  • Blockchain

Cliente

El Cliente es quien comprará bienes de otros proveedores. El propio cliente puede convertirse en vendedor y aceptará dinero de otros contra los bienes que suministra. Suponemos aquí que el cliente puede ser tanto proveedor como receptor de TPCoins. Por lo tanto, crearemos una clase de cliente en nuestro código que tiene la capacidad de enviar y recibir dinero.

Minero

El minero es quien recoge las transacciones de un grupo de transacciones y las ensambla en un bloque. El minero debe proporcionar una prueba de trabajo válida para obtener la recompensa minera. Todo el dinero que el minero recaude como tarifa será para que él se lo quede. Puede gastar ese dinero en la compra de bienes o servicios de otros proveedores registrados en la red, tal como lo hace un Cliente descrito anteriormente.

Blockchain

Finalmente, una Blockchain es una estructura de datos que encadena todos los bloques extraídos en orden cronológico. Esta cadena es inmutable y, por lo tanto, resistente al temperamento.

Puede seguir este tutorial escribiendo el código presentado en cada paso en un nuevo cuaderno de Jupyter. Alternativamente, puede descargar el cuaderno de Jupyter completo desde www.anaconda.com .

En el próximo capítulo, desarrollaremos un cliente que utiliza nuestro sistema blockchain.

Un cliente es alguien que posee TPCoins y realiza transacciones por bienes / servicios de otros proveedores en la red, incluido el suyo. Deberíamos definir unClientclase para este propósito. Para crear una identificación única a nivel mundial para el cliente, utilizamos PKI (Infraestructura de clave pública). En este capítulo, hablemos de esto en detalle.

El cliente debería poder enviar dinero desde su billetera a otra persona conocida. Del mismo modo, el cliente debería poder aceptar dinero de un tercero. Para gastar dinero, el cliente crearía una transacción especificando el nombre del remitente y la cantidad a pagar. Para recibir dinero, el cliente proporcionará su identidad al tercero, esencialmente un remitente del dinero. No almacenamos el saldo de dinero que el cliente tiene en su billetera. Durante una transacción, calcularemos el saldo real para asegurarnos de que el cliente tenga suficiente saldo para realizar el pago.

Para desarrollar el Clientclass y para el resto del código del proyecto, necesitaremos importar muchas bibliotecas de Python. Estos se enumeran a continuación:

# import libraries
import hashlib
import random
import string
import json
import binascii
import numpy as np
import pandas as pd
import pylab as pl
import logging
import datetime
import collections

Además de las bibliotecas estándar anteriores, vamos a firmar nuestras transacciones, crear hash de los objetos, etc. Para esto, necesitará importar las siguientes bibliotecas:

# following imports are required by PKI
import Crypto
import Crypto.Random
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

En el próximo capítulo, hablemos de la clase de cliente.

los Client clase genera el private y public claves usando el Python incorporado RSAalgoritmo. El lector interesado puede consultarthis tutorialpara la implementación de RSA. Durante la inicialización del objeto, creamos claves públicas y privadas y almacenamos sus valores en la variable de instancia.

self._private_key = RSA.generate(1024, random)
self._public_key = self._private_key.publickey()

Tenga en cuenta que nunca debe perder su clave privada. Para el mantenimiento de registros, la clave privada generada se puede copiar en un almacenamiento externo seguro o simplemente puede escribir la representación ASCII de la misma en una hoja de papel.

El generado publicLa clave se utilizará como la identidad del cliente. Para esto, definimos una propiedad llamadaidentity que devuelve la representación HEX de la clave pública.

@property
   def identity(self):
      return
binascii.hexlify(self._public_key.exportKey(format='DER'))
.decode('ascii')

los identityes único para cada cliente y puede estar disponible públicamente. Cualquiera podría enviarte moneda virtual usando esteidentity y se agregará a su billetera.

El código completo para Client la clase se muestra aquí -

class Client:
   def __init__(self):
      random = Crypto.Random.new().read
      self._private_key = RSA.generate(1024, random)
      self._public_key = self._private_key.publickey()
      self._signer = PKCS1_v1_5.new(self._private_key)

   @property
   def identity(self):
      return
binascii.hexlify(self._public_key.exportKey(format='DER')).decode('ascii')

Cliente de prueba

Ahora, escribiremos código que ilustrará cómo usar el Client clase -

Dinesh = Client()
print (Dinesh.identity)

El código anterior crea una instancia de Client y lo asigna a la variable Dinesh. Imprimimos la clave pública deDinesh llamando a su identitymétodo. La salida se muestra aquí:

30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001

Ahora, pasemos a crear una transacción en el siguiente capítulo.

En este capítulo, creemos un Transactionclase para que un cliente pueda enviar dinero a alguien. Tenga en cuenta que un cliente puede ser tanto un remitente como un destinatario del dinero. Cuando desee recibir dinero, algún otro remitente creará una transacción y especificará supublicdirección en él. Definimos la inicialización de una clase de transacción de la siguiente manera:

def __init__(self, sender, recipient, value):
   self.sender = sender
   self.recipient = recipient
   self.value = value
   self.time = datetime.datetime.now()

los init El método toma tres parámetros: el del remitente public clave, el destinatario publicclave, y la cantidad a enviar. Estos se almacenan en las variables de instancia para su uso por otros métodos. Además, creamos una variable más para almacenar la hora de la transacción.

A continuación, escribimos un método de utilidad llamado to_dictque combina las cuatro variables de instancia mencionadas anteriormente en un objeto de diccionario. Esto es solo para poner toda la información de la transacción accesible a través de una sola variable.

Como sabe por el tutorial anterior, el primer bloque de la cadena de bloques es un Genesisbloquear. El bloque Génesis contiene la primera transacción iniciada por el creador de la cadena de bloques. La identidad de esta persona puede mantenerse en secreto como en el caso de Bitcoins. Entonces, cuando se crea esta primera transacción, el creador puede enviar su identidad comoGenesis. Así, al crear el diccionario, comprobamos si el remitente esGenesisy si es así, simplemente asignamos algún valor de cadena a la variable de identidad; de lo contrario, asignamos la identidad del remitente alidentity variable.

if self.sender == "Genesis":
   identity = "Genesis"
else:
   identity = self.sender.identity

Construimos el diccionario usando la siguiente línea de código

return collections.OrderedDict({
   'sender': identity,
   'recipient': self.recipient,
   'value': self.value,
   'time' : self.time})

El código completo para el to_dict El método se muestra a continuación:

def to_dict(self):
   if self.sender == "Genesis":
      identity = "Genesis"
   else:
      identity = self.sender.identity

   return collections.OrderedDict({
      'sender': identity,
      'recipient': self.recipient,
      'value': self.value,
      'time' : self.time})

Finalmente, firmaremos este objeto de diccionario usando la clave privada del remitente. Como antes, usamos la PKI incorporada con el algoritmo SHA. La firma generada se decodifica para obtener la representación ASCII para imprimirla y almacenarla en nuestra blockchain. lossign_transaction el código del método se muestra aquí -

def sign_transaction(self):
   private_key = self.sender._private_key
   signer = PKCS1_v1_5.new(private_key)
   h = SHA.new(str(self.to_dict()).encode('utf8'))
   return binascii.hexlify(signer.sign(h)).decode('ascii')

Ahora probaremos esto Transaction clase.

Prueba de clase de transacción

Para ello crearemos dos usuarios, denominados Dinesh y Ramesh. Dinesh enviará 5 TPCoins a Ramesh. Para esto primero creamos los clientes llamados Dinesh y Ramesh.

Dinesh = Client()
Ramesh = Client()

Recuerde que cuando crea una instancia Client clase, la public andSe crearían claves privadas únicas para el cliente. Como Dinesh está enviando el pago a Ramesh, necesitará la clave pública de Ramesh que se obtiene utilizando la propiedad de identidad del cliente.

Por lo tanto, crearemos la instancia de transacción usando el siguiente código:

t = Transaction(
   Dinesh,
   Ramesh.identity,
   5.0
)

Tenga en cuenta que el primer parámetro es el remitente, el segundo parámetro es la clave pública del destinatario y el tercer parámetro es la cantidad a transferir. lossign_transaction El método recupera la clave privada del remitente del primer parámetro para cantar la transacción.

Una vez creado el objeto de transacción, lo firmará llamando a su sign_transactionmétodo. Este método devuelve la firma generada en formato imprimible. Generamos e imprimimos la firma usando las siguientes dos líneas de código:

signature = t.sign_transaction()
print (signature)

Cuando ejecute el código anterior, verá un resultado similar a este:

7c7e3c97629b218e9ec6e86b01f9abd8e361fd69e7d373c38420790b655b9abe3b575e343c7
13703ca1aee781acd7157a0624db3d57d7c2f1172730ee3f45af943338157f899965856f6b0
0e34db240b62673ad5a08c8e490f880b568efbc36035cae2e748f1d802d5e8e66298be826f5
c6363dc511222fb2416036ac04eb972

Ahora que nuestra infraestructura básica para crear un cliente y una transacción está lista, ahora tendremos varios clientes realizando múltiples transacciones como en una situación de la vida real.

Las transacciones realizadas por varios clientes se ponen en cola en el sistema; los mineros toman las transacciones de esta cola y las agregan al bloque. Luego extraerán el bloque y el minero ganador tendría el privilegio de agregar el bloque a la cadena de bloques y, por lo tanto, ganar algo de dinero para sí mismo.

Describiremos este proceso de minería más adelante cuando analicemos la creación de la cadena de bloques. Antes de escribir el código para múltiples transacciones, agreguemos una pequeña función de utilidad para imprimir el contenido de una transacción determinada.

Visualización de la transacción

los display_transactionLa función acepta un solo parámetro de tipo de transacción. El objeto de diccionario dentro de la transacción recibida se copia a una variable temporal llamadadict y utilizando las teclas del diccionario, los distintos valores se imprimen en la consola.

def display_transaction(transaction):
   #for transaction in transactions:
   dict = transaction.to_dict()
   print ("sender: " + dict['sender'])
   print ('-----')
   print ("recipient: " + dict['recipient'])
   print ('-----')
   print ("value: " + str(dict['value']))
   print ('-----')
   print ("time: " + str(dict['time']))
   print ('-----')

A continuación, definimos una cola de transacciones para almacenar nuestros objetos de transacción.

Cola de transacciones

Para crear una cola, declaramos un global list variable llamada transactions como sigue -

transactions = []

Simplemente agregaremos cada transacción recién creada a esta cola. Tenga en cuenta que, por brevedad, no implementaremos la lógica de gestión de colas en este tutorial.

Crear varios clientes

Ahora, comenzaremos a crear transacciones. Primero, crearemos cuatro clientes que se enviarán dinero entre ellos para obtener diversos servicios o bienes de otros.

Dinesh = Client()
Ramesh = Client()
Seema = Client()
Vijay = Client()

En este punto, tenemos cuatro clientes llamados Dinesh, Ramesh, Seema y Vijay. Actualmente asumimos que cada uno de estos clientes tiene algunos TPCoins en sus billeteras para realizar transacciones. La identidad de cada uno de estos clientes se especificaría utilizando la propiedad de identidad de estos objetos.

Creación de la primera transacción

Ahora, iniciamos nuestra primera transacción de la siguiente manera:

t1 = Transaction(
   Dinesh,
   Ramesh.identity,
   15.0
)

En esta transacción, Dinesh envía 5 TPCoins a Ramesh. Para que la transacción sea exitosa, tendremos que asegurarnos de que Dinesh tenga suficiente dinero en su billetera para este pago. Tenga en cuenta que necesitaremos una transacción de génesis para iniciar la circulación de TPCoin en el sistema. Escribirás el código de transacción para esta transacción génesis muy pronto a medida que sigas leyendo.

Firmaremos esta transacción con la clave privada de Dinesh y la agregaremos a la cola de transacciones de la siguiente manera:

t1.sign_transaction()
transactions.append(t1)

Después de la primera transacción realizada por Dinesh, crearemos varias transacciones más entre diferentes clientes que creamos anteriormente.

Agregar más transacciones

Ahora crearemos varias transacciones más, y cada transacción entregará algunos TPCoins a otra parte. Cuando alguien gasta dinero, no es necesario que verifique si hay suficientes saldos en esta billetera. De todos modos, el minero validaría cada transacción por el saldo que tiene el remitente al iniciar la transacción.

En caso de saldo insuficiente, el minero marcará esta transacción como inválida y no la agregará a este bloque.

El siguiente código crea y agrega nueve transacciones más a nuestra cola.

t2 = Transaction(
   Dinesh,
   Seema.identity,
   6.0
)
t2.sign_transaction()
transactions.append(t2)
t3 = Transaction(
   Ramesh,
   Vijay.identity,
   2.0
)
t3.sign_transaction()
transactions.append(t3)
t4 = Transaction(
   Seema,
   Ramesh.identity,
   4.0
)
t4.sign_transaction()
transactions.append(t4)
t5 = Transaction(
   Vijay,
   Seema.identity,
   7.0
)
t5.sign_transaction()
transactions.append(t5)
t6 = Transaction(
   Ramesh,
   Seema.identity,
   3.0
)
t6.sign_transaction()
transactions.append(t6)
t7 = Transaction(
   Seema,
   Dinesh.identity,
   8.0
)
t7.sign_transaction()
transactions.append(t7)
t8 = Transaction(
   Seema,
   Ramesh.identity,
   1.0
)
t8.sign_transaction()
transactions.append(t8)
t9 = Transaction(
   Vijay,
   Dinesh.identity,
   5.0
)
t9.sign_transaction()
transactions.append(t9)
t10 = Transaction(
   Vijay,
   Ramesh.identity,
   3.0
)
t10.sign_transaction()
transactions.append(t10)

Cuando ejecute el código anterior, tendrá diez transacciones en la cola para que los mineros creen sus bloques.

Transacciones de dumping

Como administrador de blockchain, es posible que desee revisar periódicamente el contenido de la cola de transacciones. Para ello, puede utilizar eldisplay_transactionfunción que desarrollamos anteriormente. Para volcar todas las transacciones en la cola, simplemente itere la lista de transacciones y para cada transacción referenciada, llame aldisplay_transaction funciona como se muestra aquí -

for transaction in transactions:
   display_transaction (transaction)
   print ('--------------')

Las transacciones están separadas por una línea discontinua para distinguirlas. Si ejecuta el código anterior, verá la lista de transacciones como se muestra a continuación:

sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c49214
4a9f463480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329e
c86794b04d773eb4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b
47e5157f8fe56c2ce3279c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311
c4d866c12d79d3fc3034563dfb0203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e
674abe7abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8ad
d126b6e1a1308fb98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa93977
04de625d1836d3f42c7ee5683f6703259592cc24b09699376807f28fe0e00ff882974484
d805f874260dfc2d1627473b910203010001
-----
value: 15.0
-----
time: 2019-01-14 16:18:01.859915
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c49214
4a9f463480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329e
c86794b04d773eb4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b
47e5157f8fe56c2ce3279c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311
c4d866c12d79d3fc3034563dfb0203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae14
3cbe59b3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fb
d9ee74b9e7ea12334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0
961b4f212d1fd5b5e49ae09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d0623
75799742a359b8f22c5362e5650203010001
-----
value: 6.0
-----
time: 2019-01-14 16:18:01.860966
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e
674abe7abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8ad
d126b6e1a1308fb98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa93977
04de625d1836d3f42c7ee5683f6703259592cc24b09699376807f28fe0e00ff882974484
d805f874260dfc2d1627473b910203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876
f41338c62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cc
e25be99452a81df4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47
452590137869c25d9ff83d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f0
0e321b65e4c33acaf6469e18e30203010001
-----
value: 2.0
-----
time: 2019-01-14 16:18:01.861958
-----
--------------

Por brevedad, he impreso solo las primeras transacciones de la lista. En el código anterior, imprimimos todas las transacciones que comienzan con la primera transacción, excepto la transacción de génesis, que nunca se agregó a esta lista. Como las transacciones se agregan a los bloques periódicamente, generalmente le interesará ver solo la lista de transacciones que aún no se han extraído. En ese caso, deberá crear unfor bucle para iterar a través de las transacciones que aún no se extraen.

Hasta ahora, ha aprendido cómo crear clientes, permitirles entre ellos y mantener una cola de las transacciones pendientes que se van a extraer. Ahora, viene la parte más importante de este tutorial y es crear una cadena de bloques en sí. Aprenderá esto en la próxima lección.

Un bloque consta de un número variable de transacciones. Para simplificar, en nuestro caso asumiremos que el bloque consta de un número fijo de transacciones, que es tres en este caso. Como el bloque necesita almacenar la lista de estas tres transacciones, declararemos una variable de instancia llamadaverified_transactions como sigue -

self.verified_transactions = []

Hemos nombrado esta variable como verified_transactionspara indicar que solo las transacciones válidas verificadas se agregarán al bloque. Cada bloque también contiene el valor hash del bloque anterior, por lo que la cadena de bloques se vuelve inmutable.

Para almacenar el hash anterior, declaramos una variable de instancia de la siguiente manera:

self.previous_block_hash = ""

Finalmente, declaramos una variable más llamada Nonce para almacenar el nonce creado por el minero durante el proceso de minería.

self.Nonce = ""

La definición completa de la Block la clase se da a continuación -

class Block:
   def __init__(self):
      self.verified_transactions = []
      self.previous_block_hash = ""
      self.Nonce = ""

Como cada bloque necesita el valor del hash del bloque anterior, declaramos una variable global llamada last_block_hash como sigue -

last_block_hash = ""

Ahora creemos nuestro primer bloque en la cadena de bloques.

Suponemos que el creador de TPCoins inicialmente entrega 500 TPCoins a un cliente conocido Dinesh. Para ello, primero crea una instancia de Dinesh:

Dinesh = Client()

Luego creamos una transacción de génesis y enviamos 500 TPCoins a la dirección pública de Dinesh.

t0 = Transaction (
   "Genesis",
   Dinesh.identity,
   500.0
)

Ahora, creamos una instancia de Block clase y llámalo block0.

block0 = Block()

Inicializamos el previous_block_hash y Nonce variables de instancia para None, ya que esta es la primera transacción que se almacena en nuestra cadena de bloques.

block0.previous_block_hash = None
Nonce = None

A continuación, agregaremos la transacción t0 anterior a la verified_transactions lista mantenida dentro del bloque -

block0.verified_transactions.append (t0)

En este punto, el bloque está completamente inicializado y listo para ser agregado a nuestra cadena de bloques. Crearemos la cadena de bloques para este propósito. Antes de agregar el bloque a la cadena de bloques, haremos hash del bloque y almacenaremos su valor en la variable global llamadalast_block_hashque declaramos previamente. Este valor será utilizado por el próximo minero en su bloque.

Usamos las siguientes dos líneas de codificación para hacer hash del bloque y almacenar el valor de resumen.

digest = hash (block0)
last_block_hash = digest

Finalmente, creamos una cadena de bloques como vemos en el siguiente capítulo.

Una cadena de bloques contiene una lista de bloques encadenados entre sí. Para almacenar la lista completa, crearemos una variable de lista llamada TPCoins -

TPCoins = []

También escribiremos un método de utilidad llamado dump_blockchainpara descargar el contenido de toda la cadena de bloques. Primero imprimimos la longitud de la cadena de bloques para saber cuántos bloques están presentes actualmente en la cadena de bloques.

def dump_blockchain (self):
   print ("Number of blocks in the chain: " + str(len (self)))

Tenga en cuenta que a medida que pasa el tiempo, la cantidad de bloques en la cadena de bloques sería extraordinariamente alta para imprimir. Por lo tanto, cuando imprima el contenido de la cadena de bloques, es posible que deba decidir el rango que le gustaría examinar. En el siguiente código, hemos impreso toda la cadena de bloques, ya que no estaríamos agregando demasiados bloques en la demostración actual.

Para recorrer la cadena, configuramos un for bucle de la siguiente manera:

for x in range (len(TPCoins)):
   block_temp = TPCoins[x]

Cada bloque referenciado se copia a una variable temporal llamada block_temp.

Imprimimos el número de bloque como un encabezado para cada bloque. Tenga en cuenta que los números comenzarían con cero, el primer bloque es un bloque de génesis que se numera cero.

print ("block # " + str(x))

Dentro de cada bloque, hemos almacenado una lista de tres transacciones (excepto el bloque génesis) en una variable llamada verified_transactions. Repetimos esta lista en unfor bucle y para cada elemento recuperado, llamamos display_transaction función para mostrar los detalles de la transacción.

for transaction in block_temp.verified_transactions:
   display_transaction (transaction)

La definición completa de la función se muestra a continuación:

def dump_blockchain (self):
   print ("Number of blocks in the chain: " + str(len (self)))
   for x in range (len(TPCoins)):
      block_temp = TPCoins[x]
      print ("block # " + str(x))
      for transaction in block_temp.verified_transactions:
         display_transaction (transaction)
         print ('--------------')
      print ('=====================================')

Tenga en cuenta que aquí hemos insertado los separadores en los puntos apropiados del código para demarcar los bloques y transacciones dentro de él.

Como ahora hemos creado una cadena de bloques para almacenar bloques, nuestra siguiente tarea es crear bloques y comenzar a agregarlos a la cadena de bloques. Para ello, agregaremos un bloque de génesis que ya ha creado en el paso anterior.

Agregar un bloque a la cadena de bloques implica agregar el bloque creado a nuestro TPCoins lista.

TPCoins.append (block0)

Tenga en cuenta que, a diferencia del resto de los bloques del sistema, el bloque de génesis contiene solo una transacción que es iniciada por el creador del sistema TPCoins. Ahora, descargará el contenido de la cadena de bloques llamando a nuestra función globaldump_blockchain -

dump_blockchain(TPCoins)

Cuando ejecute esta función, verá la siguiente salida:

Number of blocks in the chain: 1
block # 0
sender: Genesis
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100ed272b52ccb539
e2cd779c6cc10ed1dfadf5d97c6ab6de90ed0372b2655626fb79f62d0e01081c163b0864
cc68d426bbe9438e8566303bb77414d4bfcaa3468ab7febac099294de10273a816f7047d
4087b4bafa11f141544d48e2f10b842cab91faf33153900c7bf6c08c9e47a7df8aa7e60d
c9e0798fb2ba3484bbdad2e4430203010001
-----
value: 500.0
-----
time: 2019-01-14 16:18:02.042739
-----
--------------
=====================================

En este punto, el sistema blockchain está listo para usarse. Ahora permitiremos que los clientes interesados ​​se conviertan en mineros proporcionándoles una funcionalidad de minería.

Para habilitar la minería, necesitamos desarrollar una función de minería. La funcionalidad de minería debe generar un resumen en una cadena de mensaje determinada y proporcionar una prueba de trabajo. Discutamos esto en este capítulo.

Función de resumen de mensajes

Escribiremos una función de utilidad llamada sha256 para crear un resumen sobre un mensaje dado -

def sha256(message):
return hashlib.sha256(message.encode('ascii')).hexdigest()

los sha256 la función toma un message como parámetro, lo codifica en ASCII, genera un resumen hexadecimal y devuelve el valor al llamador.

Función de minería

Ahora desarrollamos el minefunción que implementa nuestra propia estrategia minera. Nuestra estrategia en este caso sería generar un hash en el mensaje dado que tiene como prefijo un número dado de unos. El número dado de unos se especifica como parámetro paramine función especificada como el nivel de dificultad.

Por ejemplo, si especifica un nivel de dificultad de 2, el hash generado en un mensaje dado debe comenzar con dos 1, como 11xxxxxxxx. Si el nivel de dificultad es 3, el hash generado debe comenzar con tres unos, como 111xxxxxxxx. Dados estos requisitos, ahora desarrollaremos la función de minería como se muestra en los pasos que se indican a continuación.

Paso 1

La función de minería toma dos parámetros: el mensaje y el nivel de dificultad.

def mine(message, difficulty=1):

Paso 2

El nivel de dificultad debe ser mayor o igual a 1, lo aseguramos con la siguiente declaración de aserción:

assert difficulty >= 1

Paso 3

Creamos un prefix variable utilizando el nivel de dificultad establecido.

prefix = '1' * difficulty

Tenga en cuenta que si el nivel de dificultad es 2, el prefijo sería "11" y si el nivel de dificultad es 3, el prefijo sería "111", y así sucesivamente. Comprobaremos si este prefijo existe en el resumen generado del mensaje. Para digerir el mensaje en sí, usamos las siguientes dos líneas de código:

for i in range(1000):
   digest = sha256(str(hash(message)) + str(i))

Seguimos agregando un nuevo número ial hash del mensaje en cada iteración y generar un nuevo resumen en el mensaje combinado. Como entrada alsha256 función cambia en cada iteración, el digestel valor también cambiaría. Comprobamos si estodigest el valor tiene por encima del conjunto prefix.

if digest.startswith(prefix):

Si se cumple la condición, cancelaremos el for bucle y devuelve el digest valor para la persona que llama.

La totalidad mine el código se muestra aquí -

def mine(message, difficulty=1):
   assert difficulty >= 1
   prefix = '1' * difficulty
   for i in range(1000):
      digest = sha256(str(hash(message)) + str(i))
      if digest.startswith(prefix):
         print ("after " + str(i) + " iterations found nonce: "+ digest)
      return digest

Para su comprensión, hemos agregado el print declaración que imprime el valor de resumen y el número de iteraciones que se necesitaron para cumplir la condición antes de regresar de la función.

Prueba de la función de minería

Para probar nuestra función de minería, simplemente ejecute la siguiente declaración:

mine ("test message", 2)

Cuando ejecute el código anterior, verá un resultado similar al siguiente:

after 138 iterations found nonce:
11008a740eb2fa6bf8d55baecda42a41993ca65ce66b2d3889477e6bfad1484c

Tenga en cuenta que el resumen generado comienza con "11". Si cambia el nivel de dificultad a 3, el resumen generado comenzará con "111" y, por supuesto, probablemente requerirá más iteraciones. Como puede ver, un minero con más poder de procesamiento podrá extraer un mensaje determinado antes. Así es como los mineros compiten entre sí para obtener sus ingresos.

Ahora, estamos listos para agregar más bloques a nuestra cadena de bloques. Aprendamos esto en nuestro próximo capítulo.

Cada minero recogerá las transacciones de un grupo de transacciones creado previamente. Para rastrear la cantidad de mensajes ya extraídos, tenemos que crear una variable global:

last_transaction_index = 0

Ahora tendremos nuestro primer minero agregando un bloque a la cadena de bloques.

Agregar el primer bloque

Para agregar un nuevo bloque, primero creamos una instancia del Block clase.

block = Block()

Recogemos las 3 transacciones principales de la cola:

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction

Antes de agregar la transacción al bloque, el minero verificará la validez de la transacción. La validez de la transacción se verifica probando la igualdad del hash proporcionado por el remitente con el hash generado por el minero utilizando la clave pública del remitente. Además, el minero verificará que el remitente tenga saldo suficiente para pagar la transacción actual.

Por brevedad, no hemos incluido esta funcionalidad en el tutorial. Una vez validada la transacción, la agregamos alverified_transactions lista en el block ejemplo.

block.verified_transactions.append (temp_transaction)

Incrementamos el índice de la última transacción para que el próximo minero recoja las transacciones posteriores en la cola.

last_transaction_index += 1

Agregamos exactamente tres transacciones al bloque. Una vez hecho esto, inicializaremos el resto de las variables de instancia delBlockclase. Primero agregamos el hash del último bloque.

block.previous_block_hash = last_block_hash

A continuación, extraemos el bloque con un nivel de dificultad 2.

block.Nonce = mine (block, 2)

Tenga en cuenta que el primer parámetro del minela función es un objeto binario. Ahora aplicamos hash a todo el bloque y creamos un resumen sobre él.

digest = hash (block)

Finalmente, agregamos el bloque creado a la cadena de bloques y reinicializamos la variable global last_block_hash para el uso en el siguiente bloque.

El código completo para agregar el bloque se muestra a continuación:

block = Block()
for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1

block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)
digest = hash (block)
TPCoins.append (block)
last_block_hash = digest

Agregar más bloques

Ahora agregaremos dos bloques más a nuestra cadena de bloques. El código para agregar los siguientes dos bloques se da a continuación:

# Miner 2 adds a block
block = Block()

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1
block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)digest = hash (block)
TPCoins.append (block)last_block_hash = digest
# Miner 3 adds a block
block = Block()

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   #display_transaction (temp_transaction)
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1

block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)
digest = hash (block)

TPCoins.append (block)
last_block_hash = digest

Cuando agregue estos dos bloques, también verá el número de iteraciones que tomó para encontrar el Nonce. En este punto, nuestra cadena de bloques consta de un total de 4 bloques, incluido el bloque de génesis.

Volcado de toda la cadena de bloques

Puede verificar el contenido de toda la cadena de bloques utilizando la siguiente declaración:

dump_blockchain(TPCoins)

Vería una salida similar a la que se muestra a continuación:

Number of blocks in the chain: 4
block # 0
sender: Genesis
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100ed272b52ccb539e2cd779
c6cc10ed1dfadf5d97c6ab6de90ed0372b2655626fb79f62d0e01081c163b0864cc68d426bbe943
8e8566303bb77414d4bfcaa3468ab7febac099294de10273a816f7047d4087b4bafa11f141544d4
8e2f10b842cab91faf33153900c7bf6c08c9e47a7df8aa7e60dc9e0798fb2ba3484bbdad2e44302
03010001
-----
value: 500.0
-----
time: 2019-01-14 16:18:02.042739
-----
--------------
=====================================
block # 1
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 15.0
-----
time: 2019-01-14 16:18:01.859915
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 6.0
-----
time: 2019-01-14 16:18:01.860966
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
value: 2.0
-----
time: 2019-01-14 16:18:01.861958
-----
--------------
=====================================
block # 2
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 4.0
-----
time: 2019-01-14 16:18:01.862946
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 7.0
-----
time: 2019-01-14 16:18:01.863932
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 3.0
-----
time: 2019-01-14 16:18:01.865099
-----
--------------
=====================================
block # 3
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
value: 8.0
-----
time: 2019-01-14 16:18:01.866219
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 1.0
-----
time: 2019-01-14 16:18:01.867223
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
recipient: 
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
value: 5.0
-----
time: 2019-01-14 16:18:01.868241
-----
--------------
=====================================

En este tutorial, hemos aprendido cómo construir un proyecto de cadena de bloques en Python. Hay muchas áreas en las que necesita agregar más funcionalidad a este proyecto.

Por ejemplo, necesitará escribir funciones para administrar la cola de transacciones. Una vez que se extraen las transacciones y el sistema acepta el bloque extraído, no es necesario que se almacenen más.

Además, los mineros ciertamente preferirían realizar las transacciones con la tarifa más alta. Al mismo tiempo, deberá asegurarse de que las transacciones con una tarifa baja o nula no se mueran de hambre para siempre.

Deberá desarrollar algoritmos para administrar la cola. Además, el tutorial actual no incluye el código de la interfaz del cliente. Deberá desarrollar esto tanto para los clientes normales como para los mineros. El proyecto de blockchain completo se ejecutaría en varias líneas más de código y está más allá del alcance de este tutorial. El lector interesado puede descargar la fuente bitcoin para un estudio más detallado.

Conclusiones

Este tutorial nítido debería ayudarlo a comenzar a crear su propio proyecto de blockchain.

Para el desarrollo completo de proyectos de blockchain, puede obtener más información de la fuente de bitcoin .

Para proyectos comerciales o no comerciales más grandes, puede considerar usar Ethereum , una plataforma de aplicaciones blockchain lista para usar.