Blog
Blockchain: Establecer concenso y desentralización
- Publicado por: Hebert
- Categoría: Blog
Hasta este punto, la cadena de bloques que implementamos está diseñada para ejecutarse en una sola computadora. A pesar de que estamos vinculando bloque con hash y aplicando la restricción de prueba de trabajo, aún no podemos confiar en una sola entidad (en nuestro caso, una sola máquina). Necesitamos que los datos se distribuyan, necesitamos múltiples nodos que mantengan la cadena de bloques. Entonces, para pasar de un solo nodo a una red de igual a igual, creemos primero un mecanismo para permitir que un nuevo nodo conozca a otros pares en la red.
Vamos a crear una variable global a la que llamaremos compañeros
e instanciamos el objeto set
, esta variable se encargará de registrar cada una de la dirección de los otros miembros participantes de la red. Esto se realiza para que las otras pc que se unan a la red puedan también tener acceso a los bloques y poder minar para desencriptar los textos:
@app.route('/registrar_nodo_amarillo', methods=['POST']) def registrar_nuevo_compañeros_amarillo(): direccion_nodo = request.get_json()["direccion_nodo"] if not direccion_nodo: return "Datos inválidos", 400 compañeros.add(direccion_nodo) return obtener_cadena_amarilla() @app.route('/registrar_con_amarillo', methods=['POST']) def registrar_con_nodo_existente_amarillo(): direccion_nodo = request.get_json()["direccion_nodo"] if not direccion_nodo: return "Datos inválidos", 400 data = {"direccion_nodo": request.host_url} headers = {'Content-Type': "application/json"} response = requests.post(direccion_nodo + "/registrar_nodo_amarillo", data=json.dumps(data), headers=headers) if response.status_code == 200: global contenedor_amarillo global compañeros volcar_cadena_amarilla = response.json()['cadena_amarilla'] contenedor_amarillo = crear_cadena_desde_volcado_azul(volcar_cadena_amarilla) compañeros.update(response.json()['compañeros']) return "Registro realizado correctamente", 200 else: return response.content, response.status_code
Un nuevo nodo que participa en la red puede invocar el método registrar_con_nodo_existente_amarillo
(a través de la ruta/registrar_con_amarillo
) para registrarse con los nodos existentes en la red. Esto ayudará con lo siguiente:
- Solicitar al nodo remoto que agregue un nuevo par a su lista de pares conocidos.
- Inicializando la cadena de bloques del nuevo nodo con la del nodo remoto.
- Resincronizando la cadena de bloques con la red si el nodo se desconecta de la red.
Sin embargo, hay un problema con múltiples nodos. Debido a la manipulación intencional o razones no intencionales (como la latencia de la red), la copia de las cadenas de algunos nodos puede diferir. En ese caso, los nodos deben acordar alguna versión de la cadena para mantener la integridad de todo el sistema. En otras palabras, necesitamos alcanzar un consenso .
Un algoritmo de consenso simple podría ser acordar la cadena válida más larga cuando las cadenas de diferentes nodos participantes en la red parecen divergir. La razón detrás de este enfoque es que la cadena más larga es una buena estimación de la mayor cantidad de trabajo realizado (recuerde que la prueba de trabajo es difícil de calcular):
class ContenedorAmarillo(): # Código anterior... @classmethod def chequear_validez_cadena(cls, cadena): resultado = True hash_bloque_anterior = "0" for bloque in cadena: hash_bloque = bloque.hash delattr(bloque, "hash") if not cls.prueba_valida(bloque, hash_bloque) or \ hash_bloque_anterior != bloque.hash_bloque_anterior: resultado = False break bloque.hash, hash_bloque_anterior = hash_bloque, hash_bloque return resultado def consenso_amarillo(): global contenedor_amarillo cadena_mas_larga = None longitud_actual = len(contenedor_amarillo.cadena) for node in compañeros: response = requests.get('{}cadena_amarilla'.format(node)) longitud = response.json()['longitud'] cadena = response.json()['cadena_amarilla'] if longitud > longitud_actual and contenedor_amarillo.chequear_validez_cadena(cadena): longitud_actual = longitud cadena_mas_larga = cadena if cadena_mas_larga: contenedor_amarillo = cadena_mas_larga return True return False
A continuación, debemos desarrollar una forma para que cualquier nodo anuncie a la red que ha extraído un bloque para que todos puedan actualizar su blockchain y pasar a extraer otras transacciones, para este caso que para este caso es el ContenedorAmarillo nuestro blockchain ya que estamos programado nuestro contenedor amarillo para el reciclaje. Otros nodos pueden simplemente verificar la prueba de trabajo y agregar el bloque minado a sus respectivas cadenas (recuerde que la verificación es fácil una vez que se conoce el nonce):
@app.route('/agregar_bloque_amarillo', methods=['POST']) def verificar_y_agregar_bloque_amarillo(): dato_bloque = request.get_json() bloque = Bloque(dato_bloque["id"], dato_bloque["transacciones"], dato_bloque["fecha"], dato_bloque["hash_bloque_anterior"], dato_bloque["nonce"]) prueba = dato_bloque['hash'] agregar = contenedor_amarillo.agregar_bloque(bloque, prueba) if not agregar: return "El bloque fue descartado por el nodo", 400 return "El bloque ha sido agregado a la cadena", 201 def anunciar_nuevo_bloque_amarillo(bloque): for compañero in compañeros: url = "{}agregar_bloque_amarillo".format(compañero) headers = {'Content-Type': "application/json"} requests.post(url, data=json.dumps(bloque.__dict__, sort_keys=True), headers=headers)
Se debe llamar al método anunciar_nuevo_bloque_amarillo
después de que el nodo extraiga cada bloque para que los pares puedan agregarlo a sus cadenas.
Estas rutas que hemos creado para nuesta API REST se pueden usar para jugar con nuestra cadena de bloques creando algunas transacciones y luego minándolas, para así verificar el funcionamiento correcto de nuestra aplicación.
Para poder verificar esto, debemos crear la app que va a interactuar con nuestra aplicación pero en este capítulo solo hablaremos de la estructura que tendrá nuestra app. Nos enfocaremos en crear una aplicación básica con Flask que se conectará a nuestra API.
Una vez se conecte, esta app realizará peticiones a nuestra API peticiones del tipo GET y del tipo POST. Básicamente nuestra app enviará y recibirá datos desde nuestra API, por otro lado es importante destacar que nuestra red Blockchain la usaremos en esta ocasión para encriptar datos de texto, estos datos de texto que vamos a encriptar se generarán desde la propia aplicación para encriptar el contenido de por ejemplo el reciclaje que realice un usuario.
Para entender un poco mejor esto, voy a poner un ejemplo de una de las aplicaciones más famosas de mensajería como es el caso de Telegram Messenger. Esta compañía utiliza su propia interfaz Blockchain para encriptar los mensajes que se envían entre sus usuarios y es por eso que es una de las redes más seguras que existen actualmente para compartir contenido.
Entonces vamos a comenzar a estructurar nuestro aplicación, en nuestra carpeta API Blockchain
tenemos nuestro archivo Blockchain_server.py
pero debemos crear ahora un archivo más llamado levantar_servidor.py
y la carpeta que contendrá nuestra aplicación, a esta carpeta la llamaremos app
dentro de esta carpeta vamos a crear los siguientes archivos, el archivo vistas.py
, __init__.py
, una carpeta llamada public
, otra llamada views
y dentro de esta carpeta views
crearemos 6 archivos, index.html
, base.html
, contenedor-amarillo.html
, contenedor-azul.html
, contenedor-verde.html
, puntaje.html
.
La estructura debería de ser la siguiente:
➡ ¡Enhorabuena si has llegado hasta aquí! Continúa aprendiendo con nuestro Curso de Blockchain: