Consulta de revocación de mandato usando Web3
Tags: web3, ethereum, javascript, solidity
Published: 2022-02-17 15:14:43

- [Aplicación web](https://consulta-popular.netlify.app/)
- [Repositorio en GitHub](https://github.com/Tepexic/consulta-popular)

Este proyecto trata de cómo se puede usar una  una aplicación web que interactúe con un contrato inteligente desplegado en la cadena de bloques (blockchain) de Ethereum para registrar y contar votos. Gráficamente, el objetivo es este:

![Arquitectura de la aplicación](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p0vfd7d4eg9hjsin1wb5.png)

El contrato recogerá la respuesta a la pregunta: 

> _¿Desea que el presidente continúe en el cargo o que renuncie?_

Las posibles respuestas son 3:
1. Sí, que continúe.
2. No, que renuncie
3. Anular voto

## Primero lo primero: ¿Qué es el blockchain?
Una definición que me gusta es:
>  _" el blockchain es un libro de contabilidad digital incorruptible de transacciones económicas que se puede programar para registrar no solo transacciones financieras, sino prácticamente todo lo que tiene valor[[1]](http://blockchain-revolution.com/)"._

El objetivo del blockchain, es que dos entidades que no se conocen ni confían una en la otra puedan realizar transacciones sin necesidad de un intermediario. Por ejemplo, cuando se hace una compra con una tarjeta de crédito o débito, es probable que ni el comprador ni el vendedor se conozcan, sin embargo, ambos saben que pueden confiar en el intermediario (banco) para realizar la transacción. En el blockchain ocurre lo mismo. Se pueden enviar y recibir transacciones, con la confianza de que serán verificadas, ejecutadas, registradas y almacenadas por la red.

### Pero, si es lo mismo, ¿para qué quiero el blockchain? 
No, no es lo mismo. En el caso del banco, se parte del supuesto de que el banco es una entidad financiera regulada por algún organismo, es decir, podemos confiar en que el banco nos va a guardar el dinero y nos lo va a dar cuando se lo solicitemos. Que cuando paguemos, el costo de lo que compramos será deducido de nuestra cuenta, que no van a desaparecer de una día para otro y llevarse nuestro dinero. Pero, ¿De dónde viene esa confianza? ¿Confiarían en un banco del que nunca han oído?

En el caso del blockchain, no hay un banco. No hay una entidad. El registro de las transacciones no está guardado en el servidor de alguna compañía, es decir, la información no está _centralizada_. Cada uno de los participantes de la red tiene una copia de la lista de transacciones, y no es posible modificarlas ni alterarlas, porque si se cambia algún registro, los demás participantes de la red notarán que ese es un registro falso. Esta es la principal razón por la que se puede aplicar a sistemas de votación.

Todos los participantes de la red(nodos) trabajan juntos para crear tablas de registros de transacciones y sellarlas con una clave única. Estas tablas se llaman _bloques_ y están unidas entre sí mediante esas claves. De ahí viene el nombre de _cadena de bloques_. 

Para incentivar a que haya computadoras trabajando en encontrar estas claves y registrar las transacciones, la red recompensa con _tokens_ a los nodos que hayan encontrado la clave. Estos tokens, son lo que conocemos como _criptomonedas_.

## Contratos inteligentes

Ethereum es un tipo especial de blockchain, no solo permite registrar transacciones, sino que también podemos poner código. Este código se conoce como _contrato inteligente_. Esto ha abierto la puerta a que se desarrollen diversas aplicaciones conocidas como _Dapps_ (por decentralized app) o aplicaciones descentralizadas. Hay de todo, hasta juegos. Uno que me gusta mucho y que les recomiendo si son fans los juegos de cartas como Magic o Yu-Gi-Oh! es [Gods Unchained](https://godsunchained.com/).

Una aplicación común de los contratos inteligentes son los sistemas de votación. El contrato es un juez absolutamente imparcial y es capaz de proporcionar un conteo inmediato de los votos registrados.

Aplicado al contexto mexicano, esto quiere decir que no sería posible usar la ya conocida técnica de la urna embarazada, debido a que cada voto registrado debe estar asociado a una persona, ni se puede _caer el sistema_, porque que la red no está concentrada en un solo sitio, sino distribuida entre todos los participantes. Sin embargo, el contrato sigue siendo vulnerable a la compra de voto y el mapacheo.

Para interactuar con un contrato inteligente, necesitamos un _monedero_, que viene siendo como una cuenta de usuario mediante la cual podemos enviar y recibir transacciones con el blockchain. Pueden leer más sobre monederos en [la página oficial de Ethereum](https://ethereum.org/es/wallets/). Personalmente, uso [Metamask](https://metamask.io/), que se instala como una extensión del navegador, e incluso tiene una versión como aplicación móvil.

## Contrato para la consulta popular
Ya con algo de contexto a la mano, a lo que nos truje:

Necesitaremos una forma de registrar un solo voto por persona. Podríamos usar la CURP, la Clave de Elector o cualquier identificador ciudadano que sea único. Si consideramos que solo las personas que cuentan con una identificación del INE pueden votar, entonces la clave de elector es la opción viable. 

Lo que debe hacer el contrato es simple: 

1. Registrar un voto por cualquiera de las 3 opciones mencionadas.
2. No permitir que una clave de elector vote más de una vez.

A continuación, dejo el código del contrato, está hecho en solidity, que es uno de los lenguajes de programación para contratos inteligentes.
```solidity
pragma solidity ^0.8.0;
// We have to specify what version of compiler this code will compile with

import "hardhat/console.sol";

contract Consulta {
    // almacenar dirección desde la que votaron y la clave de elector
    struct Votante {
        string clave;
        address addr;
        uint256 timestamp;
    }
    // Arreglo de votantes registrados, privada
    Votante[] private votantes;
    // Las opciones para votar se guardarán en una estructura con su descripción e identificador
    struct Opcion {
        string descripcion;
        uint256 id;
        uint256 votos;
    }
    // Las opciones de la consulta serán guardadas en un arreglo de Opcion
    Opcion[] private opciones;

    // Evento para cuando se emite un nuevo voto
    event NuevoVoto(string _clave, address _sender, uint256 _timestamp);

    constructor() {
        // inicializar las opciones de votos
        opciones.push(Opcion("si, que continue", 1, 0));
        opciones.push(Opcion("no, que renuncie", 2, 0));
        opciones.push(Opcion("anular voto", 3, 0));
    }

    /*
     * Obtiene las opciones de voto y el numero de votos
     */
    function getOpciones() public view returns (Opcion[] memory) {
        return opciones;
    }

    /*
     * Valida que solo exista un voto por cada clave de elector
     */
    function _validarVotoUnico(string memory _clave)
        private
        view
        returns (bool)
    {
        for (uint256 i = 0; i < votantes.length; i++) {
            if (
                keccak256(abi.encodePacked(votantes[i].clave)) ==
                keccak256(abi.encodePacked(_clave))
            ) {
                return false;
            }
        }
        return true;
    }

    function _validarOpcion(uint256 _opcion) private view returns (bool) {
        for (uint256 i = 0; i < opciones.length; i++) {
            if (opciones[i].id == _opcion) {
                return true;
            }
        }
        return false;
    }

    function votar(string memory _clave, uint256 _opcion) public {
        // Validar origen y sentido del voto
        require(_validarVotoUnico(_clave), "La clave de elector ya ha votado");
        require(
            _validarOpcion(_opcion),
            "La opcion elegida no corresponde a ninguna disponible"
        );

        // Si la opción de voto es válida y la clave no ha votado, registrar el voto
        for (uint256 i = 0; i < opciones.length; i++) {
            if (opciones[i].id == _opcion) {
                opciones[i].votos++;
                votantes.push(Votante(_clave, msg.sender, block.timestamp));
                emit NuevoVoto(_clave, msg.sender, block.timestamp);
            }
        }
    }
}
```

Las partes a destacar son las siguientes:
- El método `constructor()` se ejecuta cuando se despliega el contrato en la red, e inicializa las opciones de votación con cero votos cada una. 
- El método `votar` valida primero que la opción por la que el cliente está intentando emitir su voto sea una de las opciones que tiene registradas. Después, se revisa que esa clave de elector no haya votado previamente. Finalmente, si los chequeos anteriores fueron exitosos, se guarda la clave de elector del votante, la dirección del monedero desde el que emitió su voto y la fecha en la que ocurre el registro.

Y listo. Con eso ya podemos empezar a registrar votos. Está muy simple y se  puede mejorar en varios sentidos. Pero lo importante es que funciona. 

Para que toda la red tenga acceso al contrato, es necesario desplegarlo, que es el equivalente a poner una página web en un servidor público.

Pueden ver el contrato desplegado buscando esta dirección `0x41464D783f75fd9eE97A857730Cd665bC89A26BE` en el explorador de [la red Rinkeby](https://www.rinkeby.io/#explorer). La dirección del contrato es como su URL. Así como los monederos tienen una dirección, los contratos y las computadoras de la red, también. Es su identificador.

La red Rinkeby es una de las redes de pruebas de Ethereum. Estas redes  de pruebas se usan para proveer a los desarrolladores de un ambiente muy similar a la red principal de Ethereum para los contratos que están programando. El Ether que se intercambia en estas redes no tiene valor monetario.

Lo que se ve buscando la dirección del contrato en el explorador de bloques es algo así:
![Contrato en el explorador de Rinkeby](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnhbenpvmggwqcp4rv4b.png)

La tabla  de la parte inferior de la imagen es una lista de las transacciones (votos) que se han registrado en el contrato. Se tiene el identificador de la transacción, la dirección del monedero desde el que se votó y hasta el sentido del voto (codificado).  Esto otorga total transparencia al proceso. Si damos clic en alguna de las transacciones, podemos ver los detalles de la misma:
![Detalle de la transaccion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxbzjum4ff0rcakkg1fg.png)


## Aplicación web
También hice una aplicación web que permite registrar y leer los votos de una forma más amigable. 

Para usarla, necesitarán un monedero y, si quieren votar, algo de Ether de Rinkeby. Para obtener un monedero, [en este link hay una guía](https://www.lemmingatwork.com/inversiones/criptomonedas/que-es-metamask/), o si hablan inglés, [acá hay otra](https://metamask.zendesk.com/hc/en-us/articles/360015489531-Getting-started-with-MetaMask).

Ya que tengan el monedero, será necesario algo de Ether para poder enviar su voto. El Ether de Rinkeby no tiene valor real, y pueden obtener un poco poniendo la dirección de su monedero en [este link](https://rinkeby-faucet.com/).

![Aplicación web para votar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aedq1ck086wvscvvlg94.png)

Ahora sí, para votar, entramos a la aplicación y conectamos nuestro monedero, esto es como "iniciar sesión" en la aplicación, con la diferencia de que no necesitamos crear una cuenta, nuestro monedero es la cuenta. Verifiquen también que el monedero esté en la red Rinkeby. La aplicación les mostrará un mensaje si no es así.

![Monedero conectado](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ahq2jkl5ucdegz61ejj7.png)

Luego, hay que introducir una clave de elector válida, al menos en sintaxis, porque no tenemos otra forma de validarla. Necesitamos 6 consonantes, la fecha de nacimiento, la clave de la entidad, el sexo y otros 3 dígitos, por ejemplo: `HHRRTT90080713H100`.

![Introduzca su clave de elector](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qx83pb7t56bu2z2hkliv.png)

Luego se muestra la pregunta, y las 3 opciones por las que se puede votar. Estas opciones aparecen en orden aleatorio.

![Selección de voto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvu1y48ly2oad7e2ihs1.png)

Se generará una transacción desde nuestro monedero hacia el contrato inteligente en la red y debe ser firmada por el usuario, es decir, tenemos que autorizar a que se envíe el voto desde nuestro monedero.
![Firmar transacción](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4og0mrykowtfwqkfj08u.png)

Una vez que la autoricemos, se enviará y el blockchain iniciará el proceso de registro de la transacción, esto puede tomar varios segundos.
![Minando voto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8qb5h59sgd0fna7k6rn.png)

Ya registrado el voto en el contrato, la aplicación nos redirige a la página de resultados.

![Página de resultados](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yfge82i8bs3n2sqs9q2n.png)

Dejo aquí el link a un video de YouTube con todo el proceso de votación:
[![Consulta de revocación de mandato usando Web3](http://img.youtube.com/vi/VOI6GzrYNck/0.jpg)](http://www.youtube.com/watch?v=VOI6GzrYNck)

## Notas finales:
Este ejercicio es bastante utópico. Dependería de que todos los ciudadanos tuviesen acceso a un monedero, o a una estación de votación que contenga un monedero compartido y que esté conectada a internet. Además de que se tendría que validar que la clave de elector esté registrada ante el INE, que el monedero desde el que se vota esté autorizado a emitir votos  y un largo etcétera. Como decía al principio, esto es únicamente para mostrar una aplicación del blockchain.

### Links del proyecto
- Dirección del contrato en Rinkeby: `0x41464D783f75fd9eE97A857730Cd665bC89A26BE`
- [Aplicación web](https://consulta-popular.netlify.app/)
- [Repositorio en GitHub](https://github.com/Tepexic/consulta-popular)