0% encontró este documento útil (0 votos)
213 vistas

Blockchain en Python

Este documento introduce el desarrollo de una aplicación blockchain desde cero en Python. Explica cómo crear una blockchain pública simple implementando bloques, hashes criptográficos para hacerlos inmutables, y encadenarlos para crear una cadena de bloques. La aplicación permite a los usuarios compartir información que se almacena de forma permanente e inmutable en la blockchain.

Cargado por

belman
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
213 vistas

Blockchain en Python

Este documento introduce el desarrollo de una aplicación blockchain desde cero en Python. Explica cómo crear una blockchain pública simple implementando bloques, hashes criptográficos para hacerlos inmutables, y encadenarlos para crear una cadena de bloques. La aplicación permite a los usuarios compartir información que se almacena de forma permanente e inmutable en la blockchain.

Cargado por

belman
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 14

Desarrollar una aplicación

«blockchain» desde cero en


Python
julio 1, 2018 by Recursos Python  49 comentarios
Traducido desde el inglés Develop a blockchain application from scratch in Python.

Este tutorial introduce a desarrolladores de Python, de cualquier nivel de programación,


al blockchain. Descubrirás exactamente qué es un blockchain implementando
un blockchain público desde cero y creando una simple aplicación para desplegarlo.

Serás capaz de crear puntos de acceso para diferentes funciones del blockchain, tales como
añadir una transacción, usando el microframework Flask, y luego ejecutar los programas en
múltiples máquinas para crear una red descentralizada. También verás cómo desarrollar una
interfaz de usuario simple que interactúe con el blockchain y almacene la información para
cualquier uso, como pagos peer-to-peer, conversaciones o comercio electrónico.

Python es un lenguaje de programación fácil de entender, esa es la razón por la que lo he


elegido para este tutorial. A medida que avances con este tutorial, implementarás
un blockchain público y lo verás en acción. El código para una aplicación de ejemplo
completa, escrita usando puro Python, está en GitHub.

Obtener el código

La lógica radica en el archivo views.py. Para realmente entender blockchain desde el


principio, avancemos sobre él.

Requisitos
 Un conocimiento básico de programación en Python [véase el tutorial de Python].
 El microframework Flask (para crear puntos de acceso para el servidor blockchain).

Fundamentos

Blockchain público vs. privado

Una red blockchain pública, como la red Bitcoin, está completamente abierta al público, y
cualquiera puede unirse y participar.
En la otra mano, negocios que requieren de mayor privacidad, seguridad y velocidad en las
transacciones optan por una red blockchain privada, donde los participantes necesitan una
invitación para unirse. Saber más.
En 2008, un artículo titulado Bitcoin: un sistema de dinero electrónico peer-to-peer fue
publicado por un individuo (o tal vez un grupo) llamado Satoshi Nakamoto. El artículo
combinaba técnicas criptográficas y una red peer-to-peer sin la necesidad de confiar en una
autoridad centralizada (como los bancos) para realizar pagos de una persona a otra. Nació
Bitcoin. Además de Bitcoin, el mismo artículo introdujo un sistema distribuido para
almacenar información (ahora conocido popularmente como «blockchain»), que tiene un
campo de aplicación mucho más amplio que únicamente pagos o criptomonedas.

Desde entonces, el interés por el blockchain ha estallado en prácticamente toda la


industria. Blockchain es ahora la tecnología debajo de criptomonedas completamente
digitales como Bitcoin, tecnologías de computación distribuida como Ethereum, y
frameworks de código abierto como Hyperledger Fabric, sobre donde está montada la IBM
Blockchain Platform.

¿Qué es «blockchain»?

Blockchain es una forma de almacenar información digital. La información puede ser


literalmente cualquier cosa. Para Bitcoin, son transacciones (transferencias de Bitcoins de
una cuenta a otra), pero pueden incluso ser archivos; no importa. La información está
almacenada en forma de bloques, que están encadenados usando hashes. De aquí el nombre
«blockchain» [block = bloque, chain = cadena, «cadena de bloques»].

Toda la magia radica en el modo en que la información es añadida y almacenada en


el blockchain, que produce algunas características altamente deseables:

 Inmutabilidad de la historia.
 Invulnerabilidad del sistema.
 Persistencia de la información.
 No hay un solo punto de falla.
Ahora bien, ¿cómo es que blockchain obtiene estas características? Nos iremos acercando a
ello a medida que las vayamos implementando. Empecemos.

Acerca de la aplicación

Definamos qué es lo que hará la aplicación que estamos desarrollando. Nuestro objetivo es
construir un sitio web simple que permita a los usuarios compartir información. Puesto que
el contenido estará almacenado en el blockchain, será inmutable y permanente.

Seguiremos un procedimiento de abajo hacia arriba para implementar las cosas.


Empecemos por definir la estructura de la información que estaremos almacenando en
el blockchain. Una publicación (un mensaje publicado por cualquier usuario en nuestra
aplicación) será identificada por tres cosas esenciales:

1. Contenido
2. Autor
3. Fecha de publicación [timestamp]

1. Almacenar transacciones en bloques


Estaremos almacenando información en nuestro blockchain en un formato que es
ampliamente usado: JSON. Así es como se verá una publicación almacenada en
el blockchain:

{
"author": "nombre_del_autor",
"content": "Algunos pensamientos que el autor quiere compartir",
"timestamp": "El tiempo en el que el contenido fue creado"
}

El término genérico «información» es generlamente reemplazado en internet por el término


«transacción». Por ende, para evitar confusiones y ser consistentes, estaremos empleando el
término «transacción» para referirnos a información publicada en nuestra aplicación de
ejemplo.

Las transacciones están empaquetadas en bloques. Además, un bloque puede contener una
o más transacciones. Los bloques que contienen las transacciones son generados
frecuentemente y añadidos al blockchain. Dado que puede haber múltiples bloques, cada
uno tendría que tener un identificador único:

1. class Block:
2. def __init__(self, index, transactions, timestamp):
3. self.index = []
4. self.transactions = transactions
5. self.timestamp = timestamp

2. Hacer los bloques inmutables


Nos gustaría detectar cualquier tipo de manipulación en la información almacenada dentro
de un bloque. En blockchain, esto se hace usando una función hash.

Una función hash es una función que toma información de cualquier tamaño y a partir de
ella produce otra información de un tamaño fijo, que generalmente sirve para identificar la
entrada [input]. Aquí hay un ejemplo en Python usando la función hash sha256.

1. >>> from hashlib import sha256


2. >>> data = "Alguna informacion de longitud variada".encode("utf-8")
3. >>> sha256(data).hexdigest()
4. '7c79eb1f1853630a00ea195b8f2979f16c1ea8016d9e487e1099537c02f513d9'
5. >>> sha256(data).hexdigest() # No importa cuántas veces lo ejecutes, siempre retorna lo mismo.
6. '7c79eb1f1853630a00ea195b8f2979f16c1ea8016d9e487e1099537c02f513d9'

Las características de una función hash ideal son:


 Debería ser computacionalmente fácil de calcular.
 Incluso el cambio de un simple bit de la información debería cambiar por completo
el hash.
 No debería ser posible adivinar la información de entrada a partir del hash de salida.
Ahora sabes qué es una función hash. Vamos a guardar el hash de cada uno de los bloques
en un campo dentro de nuestro objeto Block para que actúe como una huella digital de la
información que contiene:

1. from hashlib import sha256


2. import json
3.
4. def compute_hash(block):
5. """
6. Una función que crea el hash del bloque.
7. """
8. block_string = json.dumps(self.__dict__, sort_keys=True)
9. return sha256(block_string.encode()).hexdigest()

Nota: En la mayoría de las criptomonedas, aun las transacciones individuales en el bloque


están cifradas, para formar un árbol hash (también conocido como árbol de Merkle), y la
raíz del árbol puede ser usada como el hash del bloque. No es una condición necesaria para
el funcionamiento del blockchain, por lo que lo omitiremos para mantener las cosas
ordenadas y simples.

3. Encadenar los bloques


Bien, hemos montado los bloques. El blockchain supuestamente tiene que ser una colección
de bloques. Podemos almacenar todos los bloques en una lista de Python (el equivalente a
un arreglo [array]). Pero esto no es suficiente, porque ¿qué sucedería si alguien
intencionalmente reemplaza un bloque en la colección? Crear un nuevo bloque con
transacciones alteradas, calcular el hash, y reemplazarlo por cualquier otro bloque anterior
no es una gran tarea en nuestra implementación actual, porque mantendremos la
inmutabilidad y el orden de los bloques.

Necesitamos una solución para asegurarnos que cualquier cambio en los bloques anteriores
invalide la cadena entera. Una forma de hacer esto es encadenar los bloques por su hash. Al
encadenarlos de esta forma, queremos decir incluir el hash del bloque anterior en el actual.
Así, si el contenido de cualquiera de los bloques anteriores cambia, el hash del bloque va a
cambiar, llevando a una discrepancia con el campo previos_hash en el próximo bloque.

Perfecto, cada bloque está enlazado al anterior por el campo previous_hash, ¿pero qué
sucede con el primer bloque de todos? El primer bloque es llamado el bloque génesis y es
generado manualmente o por alguna lógica única, en la mayoría de los casos. Agreguemos
el campo previous_hash a la clase Block e implementemos la estructura inicial de
nuestra clase Blockchain (ver Listado 1).

Listado 1. La estructura inicial de nuestra clase Blockchain


1. from hashlib import sha256
2. import json
3. import time
4. class Block:
5. def__init__(self, index, transactions, timestamp, previous_hash):
6. self.index = index
7. self.transactions = transactions
8. self.timestamp = timestamp
9. self.previous_hash = previous_hash
10.
11. def compute_hash(self):
12. block_string = json.dumps(self.__dict__, sort_keys=True)
13. return sha256(block_string.encode()).hexdigest()

Y aquí está nuestra clase Blockchain:

1. class Blockchain:
2.
3. def __init__(self):
4. self.unconfirmed_transactions = [] # información para insertar en el blockchain
5. self.chain = []
6. self.create_genesis_block()
7.
8. def create_genesis_block(self):
9. """
10. Una función para generar el bloque génesis y añadirlo a la
11. cadena. El bloque tiene index 0, previous_hash 0 y un hash
12. válido.
13. """
14. genesis_block = Block(0, [], time.time(), "0")
15. genesis_block.hash = genesis_block.compute_hash()
16. self.chain.append(genesis_block)
17.
18. @property
19. def last_block(self):
20. return self.chain[-1]

4. Implementar un algoritmo de prueba de


trabajo
Aunque hay un problema. Si cambiamos el bloque anterior, podemos recalcular
los hashes de todos los bloques subsiguientes sencillamente y crear un blockchain diferente
pero válido. Para prevenir esto, tenemos que hacer que la tarea de calcular un hash sea
difícil y aleatoria.

Así es como se hace. En lugar de aceptar cualquier hash para el bloque, le agregaremos


alguna condición. Agreguemos una condición que nuestro hash deba empezar con dos
ceros. Además, sabemos que a menos que cambiemos el contenido del bloque, el hash no
va a cambiar.
Por lo que vamos a introducir un nuevo campo en nuestro bloque llamado nonce.
Un nonce es un número que cambiará constantemente hasta que obtengamos un hash que
satisfaga nuestra condición. El número de ceros prefijados (el valor 2, en nuestro caso)
decide la «dificultad» de nuestro algoritmo de prueba de trabajo. Además, notarás que
nuestra prueba de trabajo es difícil de calcular pero fácil de verificar una vez que
averiguamos el nonce (para verificar, simplemente tienes que ejecutar la
función hash nuevamente).

1. class Blockchain:
2. # Dificultad del algoritmo de prueba de trabajo.
3. difficulty = 2
4.
5. """
6. Código anterior...
7. """
8.
9. def proof_of_work(self, block):
10. """
11. Función que intenta distintos valores de nonce hasta obtener
12. un hash que satisfaga nuestro criterio de dificultad.
13. """
14. block.nonce = 0
15.
16. computed_hash = block.compute_hash()
17. while not computed_hash.startswith('0' * Blockchain.difficulty):
18. block.nonce += 1
19. computed_hash = block.compute_hash()
20.
21. return computed_hash

Nótese que no hay una lógica concreta para averiguar el nonce rápidamente; es


simplemente fuerza bruta.

5. Añadir bloques a la cadena


Para agregar un bloque a la cadena, primero tendremos que verificar si la prueba de trabajo
es correcta y si el campo previous_hash del bloque a añadir luego es igual al hash del
último bloque en nuestra cadena.

Veamos el código para insertar bloques en la cadena:

1. class Blockchain:
2. """
3. Código anterior...
4. """
5. def add_block(self, block, proof):
6. """
7. Una función que agrega el bloque a la cadena luego de la verificación.
8. """
9. previous_hash = self.last_block.hash
10.
11. if previous_hash != block.previous_hash:
12. return False
13.
14. if not self.is_valid_proof(block, proof):
15. return False
16.
17. block.hash = proof
18. self.chain.append(block)
19. return True
20.
21. def is_valid_proof(self, block, block_hash):
22. """
23. Chquear si block_hash es un hash válido y satisface nuestro
24. criterio de dificultad.
25. """
26. return (block_hash.startswith('0' * Blockchain.difficulty) and
27. block_hash == block.compute_hash())

Minado

Las transacciones son almacenadas inicialmente en un conjunto de transacciones no


confirmadas. El proceso de poner transacciones no confirmadas en un bloque y calcular la
prueba de trabajo es conocido como el minado [mining] de bloques. Una vez que
el nonce que satisface nuestra condición es averiguado, podemos decir que el bloque ha
sido minado, y es colocado en el blockchain.

En la mayoría de las criptomonedas (incluyendo Bitcoin), los mineros suelen ser


premiados con alguna criptomoneda como una recompensa por usar su potencia de cálculo
para calcular la prueba de trabajo. Así es como se ve nuestra función de minado.

1. class Blockchain:
2. """
3. Código anterior...
4. """
5.
6. def add_new_transaction(self, transaction):
7. self.unconfirmed_transactions.append(transaction)
8.
9. def mine(self):
10. """
11. Esta función sirve como una interfaz para añadir las transacciones
12. pendientes al blockchain añadiéndolas al bloque y calculando la
13. prueba de trabajo.
14. """
15. if not self.unconfirmed_transactions:
16. return False
17.
18. last_block = self.last_block
19.
20. new_block = Block(index=last_block.index + 1,
21. transactions=self.unconfirmed_transactions,
22. timestamp=time.time(),
23. previous_hash=last_block.hash)
24.
25. proof = self.proof_of_work(new_block)
26. self.add_block(new_block, proof)
27. self.unconfirmed_transactions = []
28. return new_block.index

Muy bien, ya casi estamos. Puedes ver el código completo hasta este momento en GitHub.

6. Crear interfaces
Bien, ahora es momento de crear interfaces para nuestro nodo para interactuar con otros
compañeros así como con la aplicación que vamos a crear. Estaremos utilizando Flask para
crear una REST-API que interactúe con nuestro nodo. Aquí está el código para ello:

1. from flask import Flask, request


2. import requests
3.
4. app = Flask(__name__)
5.
6. # la copia del nodo del blockchain
7. blockchain = Blockchain()

Necesitamos un punto de acceso para nuestra aplicación para enviar una nueva transacción.
Éste será utilizado por nuestra aplicación para añadir nueva información (publicaciones)
al blockchain:

1. @app.route('/new_transaction', methods=['POST'])
2. def new_transaction():
3. tx_data = request.get_json()
4. required_fields = ["author", "content"]
5.
6. for field in required_fields:
7. if not tx_data.get(field):
8. return "Invlaid transaction data", 404
9.
10. tx_data["timestamp"] = time.time()
11.
12. blockchain.add_new_transaction(tx_data)
13.
14. return "Success", 201

Aquí hay un punto de acceso para retornar la copia del blockchain que tiene el nodo.
Nuestra aplicación estará usando este punto de acceso para solicitar todas las publicaciones
para mostrar.

1. @app.route('/chain', methods=['GET'])
2. def get_chain():
3. chain_data = []
4. for block in blockchain.chain:
5. chain_data.append(block.__dict__)
6. return json.dumps({"length": len(chain_data),
7. "chain": chain_data})

Y aquí hay otro para solicitar al nodo que mine las transacciones sin confirmar (si es que
hay alguna).

1. @app.route('/mine', methods=['GET'])
2. def mine_unconfirmed_transactions():
3. result = blockchain.mine()
4. if not result:
5. return "No transactions to mine"
6. return "Block #{} is mined.".format(result)
7.
8.
9. # punto de acceso para obtener las transacciones
10. # no confirmadas
11. @app.route('/pending_tx')
12. def get_pending_tx():
13. return json.dumps(blockchain.unconfirmed_transactions)
14.
15.
16. app.run(debug=True, port=8000)

Ahora, si te gustaría, puedes jugar con nuestro blockchain creando algunas transacciones y


luego minándolas usando una herramienta como cURL o Postman.

7. Establecer consenso y descentralización


El código que hemos implementado hasta ahora está pensado para ser ejecutado en una sola
computadora. Incluso aunque estamos enlazando bloques con hashes, todavía no podemos
confiarnos de una única entidad. Necesitamos múltiples nodos para mantener
nuestro blockchain. Entonces, creemos un punto de acceso para permitirle a un nodo tener
conciencia de otros compañeros en la red:

1. # la dirección de otros miembros que participan en la red


2. peers = set()
3.
4. # punto de acceso para añadir nuevos compañeros a la red.
5. @app.route('/add_nodes', methods=['POST'])
6. def register_new_peers():
7. nodes = request.get_json()
8. if not nodes:
9. return "Invalid data", 400
10. for node in nodes:
11. peers.add(node)
12.
13. return "Success", 201

Te habrás dado cuenta que hay un problema con los múltiples nodos. Debido a la
manipulación intencional o por razones inintencionales, la copia de cadenas de algunos
nodos puede diferir. En ese caso, necesitamos ponernos de acuerdo respecto de alguna
versión de la cadena para mantener la integridad de todo el sistema. Necesitamos
consensuar.

Un algoritmo simple de consenso podría ser ponernos de acuerdo respecto de la cadena


válida más larga cuando las cadenas de diferentes participantes de la red aparentan divergir.
La racionalidad debajo de este procedimiento es que la cadena más larga es una buena
estimación de la mayor cantidad de trabajo realizado:

1. def consensus():
2. """
3. Nuestro simple algoritmo de consenso. Si una cadena válida más larga es
4. encontrada, la nuestra es reemplazada por ella.
5. """
6. global blockchain
7.
8. longest_chain = None
9. current_len = len(blockchain)
10.
11. for node in peers:
12. response = requests.get('http://{}/chain'.format(node))
13. length = response.json()['length']
14. chain = response.json()['chain']
15. if length > current_len and blockchain.check_chain_validity(chain):
16. current_len = length
17. longest_chain = chain
18.
19. if longest_chain:
20. blockchain = longest_chain
21. return True
22.
23. return False

Y ahora finalmente, necesitamos desarrollar una forma para que cada nodo pueda anunciar
a la red que ha minado un bloque para que todos puedan actualizar su blockchain y seguir
minando otras transacciones. Otros nodos pueden simplemente verificar la prueba de
trabajo y añadirla a sus respectivas cadenas:

1. # punto de acceso para añadir un bloque minado por alguien más a la cadena del nodo.
2. @app.route('/add_block', methods=['POST'])
3. def validate_and_add_block():
4. block_data = request.get_json()
5. block = Block(block_data["index"], block_data["transactions"],
6. block_data["timestamp", block_data["previous_hash"]])
7.
8. proof = block_data['hash']
9. added = blockchain.add_block(block, proof)
10.
11. if not added:
12. return "The block was discarded by the node", 400
13.
14. return "Block added to the chain", 201
15.
16. def announce_new_block(block):
17. for peer in peers:
18. url = "http://{}/add_block".format(peer)
19. requests.post(url, data=json.dumps(block.__dict__, sort_keys=True))

El método announce_new_block debería ser llamado luego que un bloque ha sido minado


por el nodo, para que los compañeros lo puedan añadir a sus cadenas.

8. Crear la aplicación
Bien, el backend está listo. Puedes ver el código hasta este momento en GitHub.

Ahora, es tiempo de empezar a trabajar en la interfaz de nuestra aplicación. Hemos usado


plantillas Jinja2 para hacer las páginas de la web y un poco de CSS para que se vea
agradable.

Nuestra aplicación necesita conectarse a un nodo en la red blockchain para obtener la


información y también para enviar nueva. Puede haber múltiples nodos también:

1. import datetime
2. import json
3.
4. import requests
5. from flask import render_template, redirect, request
6.
7. from app import app
8.
9.
10. CONNECTED_NODE_ADDRESS = "http://127.0.0.1:8000"
11.
12. posts = []

La función fetch_posts obtiene la información de la URL /chain del nodo, la procesa y


la almacena localmente.

1. def fetch_posts():
2. get_chain_address = "{}/chain".format(CONNECTED_NODE_ADDRESS)
3. response = requests.get(get_chain_address)
4. if response.status_code == 200:
5. content = []
6. chain = json.loads(response.content)
7. for block in chain["chain"]:
8. for tx in block["transactions"]:
9. tx["index"] = block["index"]
10. tx["hash"] = block["previous_hash"]
11. content.append(tx)
12.
13. global posts
14. posts = sorted(content, key=lambda k: k['timestamp'],
15. reverse=True)
La aplicación tiene un formulario HTML para tomar la entrada del usuario y luego realiza
una petición POST a un nodo conectado para añadir la transacción al conjunto de
transacciones sin confirmar. La transacción luego es minada por la red, y finalmente será
obtenida una vez que recarguemos nuestro sitio web:

1. @app.route('/submit', methods=['POST'])
2. def submit_textarea():
3. """
4. Punto de acceso para crear una nueva transacción vía nuestra
5. aplicación.
6. """
7. post_content = request.form["content"]
8. author = request.form["author"]
9.
10. post_object = {
11. 'author': author,
12. 'content': post_content,
13. }
14.
15. # Submit a transaction
16. new_tx_address = "{}/new_transaction".format(CONNECTED_NODE_ADDRESS)
17.
18. requests.post(new_tx_address,
19. json=post_object,
20. headers={'Content-type': 'application/json'})
21.
22. return redirect('/')

9. Ejecutar la aplicación
¡Está lista! Puedes encontrar el código final en GitHub.

Para ejecutar la aplicación:

1. Inicia el servidor del nodo blockchain:


python node_server.py
2. Ejecuta la aplicación:
python run_app.py
Deberías ver la aplicación corriendo en http://localhost:5000.

1. Intenta enviando algo de información, y deberías ver algo como esto:


2. Presiona el botón Request to mine, y deberías ver algo así:

3. Presiona el botón Resync, y deberías ver la aplicación resincronizando con la cadena:

Verificar transacciones

Tal vez hayas notado una falla en la aplicación: cualquiera puede cambiar cualquier nombre
y publicar cualquier contenido. Una forma de resolver esto es creando los usuarios
usando criptografía de clave pública-privada. Cada usuario nuevo necesita una clave
pública (análoga a un nombre de usuario) y una clave privada para ser capaces de publicar
en nuestra aplicación. Las claves actúan como una firma digital. La clave pública solo
puede decodificar el contenido encriptado por la correspondiente clave privada. Las
transacciones serán verificadas usando la clave pública del autor antes de añadir cualquier
bloque. De esta manera, sabremos exactamente quién escribió el mensaje.

Conclusión
Este tutorial cubrió los fundamentos de un blockchain público. Si lo seguiste
completamente, has implementado un blockchain desde cero y creado una simple
aplicación permitiendo a los usuarios compartir información en él.

También podría gustarte