Documentación Voto BlockChain

 

  Descargar Proyecto

Descripción del Problema:

Los sistemas electrónicos de votación conocidos hasta ahora sufren un problema de seguridad y confianza grave al ser centralizados (arquitectura cliente servidor), lo cual genera el rechazo de estos sistemas. Esto es solucionado con el sistema tradicional de voto que involucra soluciones no sustentables y engorrosas como la papeleta que además trae problemas de ambigüedad (votos objetados) y participación.

 

Análisis del Problema:

Para comprender el sistema, se debe hacer una división por componentes:

Blockchain

Blockchain es una estructura de dato que permite que los datos publicados en al red sean perpetuos, esto es porque un atacante que quiera cambiar los datos debería recalcular toda la cadena y publicar un nuevo bloque más rápido de lo que otro nodo neutro publique un nuevo bloque, lo cual es altamente improbable.

Para entender blockchain como una estructura de datos se debe primero introducir un bloque, el bloque es una estructura con dos componentes: un header y un payload, el payload tiene la información útil y el header tiene la información que caracteriza al bloque y la cadena según el protocolo, pero dentro de todos ellos va un dato fijo que es el hash del bloque, que actúa como una especie de firma digital del mismo:

{

payload: {

<aqui el payload>

}

header: {

hash: aab45711c5a2

}

}

para formar una cadena sebe incluir además el hash del bloque anterior, el hash del bloque actual dependerá del payload y del hash del bloque anterior, por lo que, si el contenido actual del bloque cambia en lo más mínimo, o si el bloque anterior es alterado los hashes no coincidirán y deberán ser recalculados:

{

payload: {

<aqui el payload>

}

header: {

hash_anterior: aab45711c5a2

hash: bc67e9923111

}

}

La idea general es comparar estos bloques entre varios nodos, si un nodo intenta publicar un bloque modificado o que no respeta el protocolo el resto de nodos podrá rechazarlo.

Este protocolo tiene como propósito designar al nodo que publicará el próximo bloque y además dicta las reglas de cómo debe ser construido. La parte en que los nodos se ponen de acuerdo de quien publicará el siguiente bloque entre los pares se llama "método de consenso" y la forma en como se transmite estos bloque entre nodos es mediante la forma p2p.

p2p:

En nuestro proyecto diseñamos la red p2p al no encontrar una librería simple en java. La idea general de p2p es que los nodos actúan tanto como clientes y servidores al mismo tiempo, por lo cual hacen y pueden recibir peticiones.

Cada nodo tiene un rol, el cual puede ser Tracker o Worker.

El Traker es el nodo que conoce y puede llegar a otros Trakers y que tiene cierta cantidad de Workers asociados a él, por lo cual cohesiona la red. Puede hacer stream de un mensaje al resto de trakers o a sus Workers. Puede unir y deslogear Trakers y Workers y puede ejecutar ciertas atribuciones del método de consenso que se explicarán más tarde. 

El Worker por su parte es quien hace el trabajo pesado, es quien recibe la carga útil a agregar al bloque, es quien construye y verifica los bloques y la cadena. Cada uno está asociado a un Traker, por lo cual el esquema queda de la forma:

 

Método de consenso:

En las cryptomonedas se usa el hecho de que se distribuye valor, es decir, el contenido de los bloques son activos y por lo cual se le entregan incentivos a quienes crean el bloque y perpetúan la cadena mediante el método de consenso (puede interesar los métodos Proof of Work y Proof of Stack). Existe un proyecto llamado Cardano que en su cryptomoneda llamada ADA usa un método de consenso llamado Ouroboros: más información acá, La idea general de Ouroboros es que se crean los bloques durante épocas que son ciclos de tiempo fijo definidos en el protocolo y es un "lider de época" el encargado de conducir el proceso de creación del bloque. Este líder es elegido de manera aleatoria mediante una "sharedseed".

La implementación de este protocolo es un tanto complicada (código fuente) por lo cual hicimos uan simplificación, nuestra red elige de manera aleatoria a un tracker, y este a su vez elije a uno de sus Worker para generar el próximo bloque. Luego, cuando el bloque es construido, es publicado en la red y el Tracker elegido reinicia la votación. 

Este proceso tiene ciertas consideraciones.

Bizantine Fault Tolerance

La tolerancia de fallo bizantino es la tolerancia que tiene la red para el problema de los generales bizantinos (Una buena explicación acá), que básicamente es, una falla producida por un comportamiento no esperado de alguno de los nodos. Para ello el protocolo de la red debe tener ciertas restricciones. Existen 2 tipos de ataques/problemas de los nodos: 

  1. Que el nodo responda con mensajes o datos fuera del protocolo
  2. Que el nodo no responda, o tarde en responder.

Para el primero es el protocolo de Blockchain y el sistema de verificación de votos el que interviene al momento de generar los datos. Pero además esto se debe considerar para el sistema de generación de bloques, más específicamente al momento, de elegir al Tracker que generará el siguiente bloque. El segundo caso es más sencillo y se resuelve mediante timeouts, si un nodo demora en responder, es ignorado y el protocolo sigue con lo suyo. Para estas dos consideraciones se debe tener un cierto porcentaje de nodos que se porten correctamente, este porcentaje es la tolerancia de fallo bizantino y depende del método de consenso. En nuestro caso nuestra tolerancia es del 50%+1 de la red.

Elección del Tracker

La elección del Tracker debe prever que los nodos tengan comportamientos no esperados descritos en el apartado de Bizantine Fault Tolerance. Por lo tanto se hace en varias etapas. Primero los Trackers emiten sus votos y hacen un Stream de ellos para todos los otros Trackers, el voto es un número aleatorio entre el menor de los Ids de los Tracker y el mayor (ejemplo: entre 100 y 107 para una red de 8 trackers). Los Trackers agregan todos los votos recibidos, incluido el suyo en una lista con la ID del tracker y su voto:

{

100: 101

101: 100

102 :103

103 : 101 

}

Esta lista es publicada por Stream a todos los Trackers. Una vez que el Tracker tiene cierto % de las tablas en su poder elimina la tabla que tiene valores inconsistentes, o elimina el id de todas las tablas que tiene distintos votos. Una vez limpiada las tablas de datos inconsistentes todas las tablas restantes deberían ser iguales. Se elije cualquier (que es la misma) y se suman todos los valores y al resultado se le saca el módulo del total de trackers:

Sum(voto1,voto2,voto3,voto4) % 4 = winner

El resultado es el Tracker elegido. Como todos los Tracker bien intencionados tienen la misma tabla el ganador es el mismo para todos y todos esperarán el bloque proveniente del ganador. Mientras que el ganador se identifica a si mismo y elije a uno de sus Worker para que genere el siguiente bloque. El ganador tiene cierto tiempo límite para publicar el nuevo bloque, una vez que lo publica llama a votaciones entre Trackers de nuevo. Si el tiempo límite pasa, todos los Trackers bien intencionados se llaman a si mismos a votación, por lo tanto la votación se reinicia naturalmente con los últimos participantes de la elección anterior.

Un esquema simplificado entre dos Trackers se muestra a continuación:

 

Una vez que es publicado el siguiente bloque los Workers verifican el bloque, si está correcto lo agregan a la cadena, si no, es desechado. Pero también hay que verificar el contenido que en este caso son los votos. Los votos corresponden al segundo módulo.

Verificación de los votos:

Para verificar los votos se debe tener en cuenta las posibles amenazas:

Es por esto que se diseñó un sistema a través de firmas digitales basado en las transacciones "pay to public hash" de bitcoin (acá una explicación).

Supóngase una máquina que se encargue de procesar y publicar los votos, y dentro tenga un par llave pública y privada, en nuestro caso basado en el algoritmo rsa con encriptación SHA-256 (breve explicación). Cada vez que un votante ingrese a la máquina lo hará a través de un testigo (su RUT, una clave, un código QR, un dato biométrico, etc) en este caso su RUT, y luego hace la elección de su candidato, el cual será el "valor del voto". internamente la maquina hará un proceso de firma del voto:

En este caso, la máquina puede esperar más votos antes de publicar el voto, nosotros optamos que los votos individuales e publican inmediatamente, para publicarlo la máquina hace otro proceso de firmado:

Estos datos, junto a los datos del voto generan un input, que son parte del payload del bloque en la cadena de bloque. Este input se transmite a un Worker. El siguiente esquema ilustra este proceso:

 

Para verificar el voto el Worker realzia lo siguiente:

revisa que el address de la máquina está listado en el bloque génesis, si no es asi TODO el input es descartado, porque significa que el voto fue emitido por una máquina no inscrita(no controlada) por el ente que lleva a cabo el proceso de votación. Si encuentra el address, aplica la función hash sobre la llave pública de la máquina puesta en el input, si coincide con el address de la máquina significa que la llave pública pertenece a una máquina inscrita. Luego con el address y la llave pública se verifica la firma digital de la máquina, esto prueba que la llave pública tiene su par privado.

Para verificar los votos, el Worker revisa el address de cada votante y los busca en el bloque génesis, si no lo encuentra significa que quien emitió el voto no está en el padrón electoral, (es menor de edad, no está isncrito o sencillamente no existe) y el voto es descartado (sólo el voto no el input entero). Luego toma la llave pública de la máquina y el address del votante y verifica la firma digital del votante, si se confirma significa que el voto fué emitido en una máquina validada ya que para generar la firma se debía tener la llave privada, en definitiva, que el voto fue hecho en presencia de una llave privada. Con esto el valor del voto es contado.

Luego que se hacen todas estas verificaciones en cada voto del input y de corregir el input por si algún voto no es válido, este es agregado a los inputs del bloque (que pueden ser varios) y estos inputs son agregados al payload del bloque.

Terminales de interacción:

Ya con todo y programados los Workers con los Trackers, la red blockchain podría operar, pero falta perfilar la máquina que genera los votos y hace un push de ellos a la red, y el primer nodo que genera el primer bloque.

Por otra parte queda la interrogante de cómo los bloques son pasados entre los nodos. Las firmas digitales, los votos, los inputs y los bloques son descritos por distintas estructuras de datos y objectos, por ejemplo las firmas digitales son arreglos de byte, los bloques son HashMaps, etc. Para transmitir  estos objetos por red deben ser serializados, y como son hartos nodos en la red se debe reducir la cantidad de datos que se envían de modo de no saturar la red con datos. Es por esto que se crea un método de serialización especial para este caso.

 

Los bloques son codificado de la siguiente manera:

El nonce(number once) y prev_nonce son números enteros usados para que lso hash y prev_hash empeicen con 00 (Hash(hash+nonce)=00hash). Esto fue así para una prueba de método de concenso (Proof of Work) que fue reemplazada. En este proyecto el nonce no hace nada, pero se mantuvo porque era una buena forma de debugear los hashes.

Asi un bloque se formaria de la forma:

número_de_inputs(+)tamaño_del_address_de_la_máquina(+)addres_de_la_máquina(+)....(+)cantidad_de_votos_en_el_input(+)tamaño_del_address_del_primer_voto(+)address_del_primer_voto(+)...(+)prev_hash(+)0000prev_nonce(+)Hash(+)0000nonce

(+)= concatenar

... = asi con todos los datos de la mquina y los votos.

Existen 2 tipos de terminales de interacción, el que general el bloque génesis y sirve para ver las estadísticas de la cadena, y el que genera los votos.

Consola de administración.

El bloque génesis no es más que un bloque cuyo payload tiene la lista de las máquinas habilitadas para emitir votos, el padrón electoral y el código de los candidatos. Este bloque es cargado a una consola de administración que funciona como el priemr Tracker de la red al cual los otros Tracker son conectados.

Una vez que el bloque génesis es publicado en la red y se genera el priemr bloque (vacío o no), la consola de administración ya no puede interferir y puede irse offline sin intervenir en el proceso.

Las siguientes muestran la consola de administración en donde se ve el bloque génesis en su formato de diccionario y su formato serializado, además de las estadísticas.

 

Consola de Votación:

La consola de votación primero pregunta por el RUT, una vez ingresado muestra las opciones por las cuales votar. Hecho el voto hace un push de el al worker que se le fue asignado.

 

Casos de uso

 

Lo interesante de BlockChain es que en el payload se puede poner lo que sea y la arquitectura externa puede seguir funcionando siempre y cuando estén en clases independientes. En este proyecto no tocamos el tema de lso contratos inteligentes pero el sistema de verificación de votos es un proto-contrato inteligente y los contratos inteligente dan pie a las dapps (distributed application) y estas a su vez dan número a un sin fín de aplicaciones .

BlockChain puede ocuparse para: cryptomonedas, poderes judiciales y archivos notariales firmados digitalmente, sistemas de votación, verificación de activos a activos digitales, verificación y transparencia de información (véase energía abierta), entre muchos otros.

Nuestro proyecto en específico puede aplicarse a:

 

Pruebas:

 

El uso se muestra a continuación:

Se inicia el Tracker génesis al cual se le conecta otro Tracker, se puede observar que "Trackers en la red" dice 2.

En la siguiente imagen se observa el proceso de elección del Tracker, dónde se lee el mensaje "I'm the winner :)" significa que ese Tracker fue elegido para pedirle a su worker que haga el siguiente blo que.

En esta imagen vemos a los 2 Workers, se observa que el de la derecha está generando el próximo Bloque.

Se ven los bloques generados (vacíos)

 

Se ven lso bloques geenrados en su formato serializados

La consola de votación en donde se vota por Juan Carlos Bodoque

Los Workers publicando el voto recibido

Acá se ve que en la cadena se agregó un bloque con un input.

El voto contabilizado

 

Malas prácticas:

En Java los HashMaps son sólo de un tipo, por lo cual para guardar distintos objetos en un HashMap estos deben ser guardados como Objetos genéricos en un HashMap<String,Object> y al recuperarse deben ser recasteados a su clase. Esto es una pésima práctica pero se dejó así al no encontrarse una pronta solución. El programa funciona pero no óptimamente por esto.

Al ser HashMaps, de listas, de hashmaps, de arrays, a veces para buscar y comparar valores se debe usar un algoritmo que presenta una complejidad n^3, esto es debido a la pobre construcción de los datos estructurados.

 

Dificultades:

 

Diagrama de Clases