Blockchain Aplicado Al Voto Digital (Cámara de Diputados)
Prototipo de sistema de voto digital basado en blockchain, desarrollado y premiado en un hackathon realizado en la Cámara de Diputados.
Problem
Los sistemas de votación tradicionales enfrentan desafíos críticos en términos de transparencia, confianza y verificabilidad. En contextos políticos y gubernamentales, cualquier falla —técnica o perceptual— puede comprometer la legitimidad de los resultados y la confianza ciudadana.
Además, muchas propuestas de voto digital no logran equilibrar tres factores clave: seguridad, anonimato y auditoría pública, especialmente en entornos políticamente sensibles.
Solution
Se diseñó un prototipo de voto digital descentralizado basado en una arquitectura conceptual que integra blockchain, Zero-Knowledge Proofs (zk) y Smart Contracts, garantizando:
- Inmutabilidad de los votos emitidos
- Anonimato del votante
- Trazabilidad y auditoría pública de los resultados
- Reducción de intermediarios y puntos únicos de falla
- Portabilidad entre diferentes ecosistemas blockchain
El proyecto fue desarrollado y presentado durante un hackathon realizado en la Cámara de Diputados, donde resultó ganador tras más de 30 horas continuas de trabajo, validando tanto su solidez técnica como su viabilidad institucional.
Modelo Conceptual: Separación de Capas
La premisa central del sistema es separar de forma estricta la identidad del votante, la emisión del voto y la gobernanza del proceso, logrando un equilibrio entre seguridad, anonimato y auditabilidad pública.
En este modelo:
- La blockchain no "conoce" a las personas, solo verifica reglas
- Zero-Knowledge Proofs permiten demostrar legitimidad sin exponer información sensible
- Los Smart Contracts actúan como árbitros neutrales del proceso
- La arquitectura es agnóstica a la blockchain, permitiendo implementaciones en EVM y Substrate
Technical Approach
1. Identidad y Elegibilidad (Capa zk – Off-Chain)
Toda la información sensible ocurre fuera de la blockchain. Antes de emitir un voto, el ciudadano genera una prueba criptográfica Zero-Knowledge que demuestra que:
- Pertenece al padrón electoral
- Está habilitado para votar
- No ha votado previamente
Esta prueba se construye contra un conjunto autorizado (por ejemplo, un Merkle Tree del padrón), pero sin revelar identidad, nombre, dirección blockchain ni datos personales. El resultado es una afirmación verificable del tipo: "Cumplo las reglas del sistema" sin decir quién las cumple.
Aquí, la confianza no se deposita en una autoridad central, sino en la validez matemática de la prueba.
2. Emisión del Voto (Capa Blockchain – Smart Contract)
El smart contract VotingSystem actúa como el árbitro neutral del proceso electoral. Su responsabilidad no es identificar votantes, sino ejecutar reglas inmutables.
La arquitectura fue implementada en dos ecosistemas blockchain diferentes, demostrando la portabilidad del diseño:
Implementación EVM (Solidity)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract VotingSystem {
// Estructura del voto
struct Vote {
uint256 folio; // Identificador único institucional
uint256 candidateId; // ID del candidato seleccionado
bool isCoalition; // Indica si es voto en coalición
address voter; // Dirección técnica (no identidad civil)
uint256 timestamp; // Momento de emisión
}
// Estado del sistema
mapping(uint256 => Vote) public votes; // Registro de votos
mapping(uint256 => bool) public usedFolios; // Control de folios utilizados
mapping(uint256 => uint256) public voteCounts; // Conteo por candidato
mapping(address => bool) public validators; // Validadores autorizados
uint256 public totalVotes; // Total de votos emitidos
bool public votingOpen; // Estado de la votación
uint256 public validatorCount; // Cantidad de validadores
uint256 public closeConfirmations; // Confirmaciones para cierre
address public admin;
// Eventos públicos e inmutables
event VoteCast(uint256 indexed folio, uint256 candidateId, bool isCoalition);
event VotingClosed(uint256 totalVotes);
event ValidatorAdded(address validator);
modifier onlyAdmin() {
require(msg.sender == admin, "Solo el administrador");
_;
}
modifier onlyValidator() {
require(validators[msg.sender], "Solo validadores");
_;
}
modifier votingIsOpen() {
require(votingOpen, "Votacion cerrada");
_;
}
constructor() {
admin = msg.sender;
votingOpen = true;
}
function addValidator(address _validator) external onlyAdmin {
require(!validators[_validator], "Ya es validador");
validators[_validator] = true;
validatorCount++;
emit ValidatorAdded(_validator);
}
function castVote(
uint256 _folio,
uint256 _candidateId,
bool _isCoalition
) external votingIsOpen {
require(!usedFolios[_folio], "Folio ya utilizado");
require(_candidateId > 0, "Candidato invalido");
votes[totalVotes] = Vote({
folio: _folio,
candidateId: _candidateId,
isCoalition: _isCoalition,
voter: msg.sender,
timestamp: block.timestamp
});
usedFolios[_folio] = true;
voteCounts[_candidateId]++;
totalVotes++;
emit VoteCast(_folio, _candidateId, _isCoalition);
}
function confirmClose() external onlyValidator {
require(votingOpen, "Ya esta cerrada");
closeConfirmations++;
if (closeConfirmations >= validatorCount) {
votingOpen = false;
emit VotingClosed(totalVotes);
}
}
function getResults(uint256 _candidateId) external view returns (uint256) {
return voteCounts[_candidateId];
}
function isFolioUsed(uint256 _folio) external view returns (bool) {
return usedFolios[_folio];
}
}
Implementación Substrate (Rust)
use frame_support::{
decl_module, decl_storage, decl_event, decl_error, dispatch,
traits::Get,
};
use frame_system::ensure_signed;
use sp_std::prelude::*;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
}
// Estructura equivalente a la versión Solidity
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct Vote<AccountId> {
folio: u32,
candidate_id: u32,
voter: AccountId,
is_coalition: bool,
}
// Storage: equivalente a los mappings de Solidity
decl_storage! {
trait Store for Module<T: Config> as VotingSystem {
// Control de folios utilizados
FolioUsed get(fn folio_used): map hasher(blake2_128_concat) u32 => bool;
// Registro de votos por folio
Votes get(fn votes): map hasher(blake2_128_concat) u32 => Vote<T::AccountId>;
// Control de votantes (dirección ya votó)
HasVoted get(fn has_voted): map hasher(blake2_128_concat) T::AccountId => bool;
// Lista de validadores autorizados
Validators get(fn validators): Vec<T::AccountId>;
// Contador de confirmaciones para cierre
ValidatorCount get(fn validator_count): u32;
// Estado de la votación
VotingClosed get(fn voting_closed): bool;
// Tiempo de inicio (adicional en Substrate)
VotingStartTime get(fn voting_start_time): T::BlockNumber;
}
}
// Eventos equivalentes a los de Solidity
decl_event!(
pub enum Event<T> where AccountId = <T as frame_system::Config>::AccountId {
VoteCast(u32, u32, AccountId, bool),
VotingClosed(),
}
);
// Manejo de errores tipado
decl_error! {
pub enum Error for Module<T: Config> {
NotValidator,
VotingIsClosed,
FolioAlreadyUsed,
AlreadyVoted,
InvalidCandidateId,
}
}
// Funciones extrinsics (equivalente a funciones públicas)
decl_module! {
pub struct Module<T: Config> for enum Call where origin: T::Origin {
type Error = Error<T>;
fn deposit_event() = default;
// Emitir voto (equivalente a castVote en Solidity)
#[weight = 10_000]
pub fn cast_vote(
origin,
folio: u32,
candidate_id: u32,
is_coalition: bool
) -> dispatch::DispatchResult {
let sender = ensure_signed(origin)?;
// Validaciones equivalentes a los require() de Solidity
ensure!(!Self::voting_closed(), Error::<T>::VotingIsClosed);
ensure!(!Self::folio_used(folio), Error::<T>::FolioAlreadyUsed);
ensure!(!Self::has_voted(&sender), Error::<T>::AlreadyVoted);
ensure!(candidate_id > 0, Error::<T>::InvalidCandidateId);
// Crear estructura de voto
let vote = Vote {
folio,
candidate_id,
voter: sender.clone(),
is_coalition,
};
// Actualizar estado
<FolioUsed>::insert(folio, true);
<Votes<T>>::insert(folio, vote);
<HasVoted<T>>::insert(&sender, true);
// Emitir evento
Self::deposit_event(RawEvent::VoteCast(folio, candidate_id, sender, is_coalition));
Ok(())
}
// Cerrar votación con gobernanza distribuida
#[weight = 10_000]
pub fn close_voting(origin) -> dispatch::DispatchResult {
let sender = ensure_signed(origin)?;
// Solo validadores pueden confirmar cierre
ensure!(Self::is_validator(&sender), Error::<T>::NotValidator);
// Incrementar contador de confirmaciones
let count = Self::validator_count().saturating_add(1);
ValidatorCount::put(count);
// Cerrar solo cuando todos los validadores confirman
if count == Self::validators().len() as u32 {
VotingClosed::put(true);
Self::deposit_event(RawEvent::VotingClosed());
}
Ok(())
}
}
}
// Funciones auxiliares
impl<T: Config> Module<T> {
fn is_validator(address: &T::AccountId) -> bool {
Self::validators().contains(address)
}
}
Comparación de Implementaciones
| Aspecto | Solidity (EVM) | Rust (Substrate) |
| ----------------- | ------------------------------ | ---------------------------- |
| Ecosistema | Ethereum, Polygon, BSC | Polkadot, Kusama, parachains |
| Tipo de dato | `address` (identidad on-chain) | `AccountId` (tipo genérico) |
| Storage | `mapping` (clave → valor) | `StorageMap` (con hasher) |
| Validación | `require()` (condición) | `ensure!()` (macro) |
| Seguridad | Gas, guards reentrancy | Weight, tipado fuerte |
| Eventos | `emit Event()` | `deposit_event()` |
| Modificadores | `modifier` (pre/post lógica) | Lógica directa en función |
| Errores | `revert` (string) | `enum` tipado |
Ventajas de la implementación en Rust/Substrate:
- Seguridad de tipos: El compilador de Rust previene clases enteras de bugs
- Sistema de pesos: Control más granular de recursos computacionales
- Errores tipados: Mejor manejo y debugging de errores
- Parachain-ready: Compatible con ecosistema Polkadot
- Sin gas variable: Costos de transacción más predecibles
Ventajas de la implementación en Solidity/EVM:
- Ecosistema maduro: Más herramientas y auditorías disponibles
- Mayor adopción: Más desarrolladores familiarizados con EVM
- Interoperabilidad: Compatible con múltiples cadenas EVM
- Tooling establecido: Hardhat, Truffle, OpenZeppelin
Análisis Arquitectónico Común
Ambas implementaciones respetan los mismos principios fundamentales:
Estructura de Datos Mínima:
// Solidity
struct Vote {
uint256 folio;
uint256 candidateId;
bool isCoalition;
address voter;
uint256 timestamp;
}
// Rust
pub struct Vote<AccountId> {
folio: u32,
candidate_id: u32,
voter: AccountId,
is_coalition: bool,
}
Restricciones Inmutables:
Ambas versiones implementan las mismas validaciones críticas:
- Un folio no puede reutilizarse
- Un votante no puede votar dos veces
- La votación debe estar abierta
- El candidato debe ser válido
Gobernanza Distribuida:
Ambas implementan el mismo mecanismo de cierre multi-firma:
- Se requiere confirmación de todos los validadores
- Ningún actor individual puede cerrar unilateralmente
- El proceso es transparente y auditable
3. El Folio como Puente Institucional
El uso de un folio cumple un rol clave para contextos gubernamentales y electorales reales:
- Puede existir previamente en sistemas físicos o digitales
- Puede ser validado dentro de la prueba zk
- Permite trazabilidad institucional sin comprometer anonimato
Flujo completo con zk (agnóstico a la blockchain):
- El ciudadano presenta su folio + credencial electoral
- Sistema off-chain genera prueba zk que valida:
- Folio pertenece al padrón
- Ciudadano está habilitado
- Folio no ha sido usado
- Se genera un nullifier criptográfico (hash irreversible)
- El contrato (EVM o Substrate) verifica el nullifier
- Voto se registra sin revelar identidad
Esto conecta el sistema descentralizado con procesos electorales tradicionales, facilitando adopción, auditoría y confianza pública.
4. Portabilidad y Escalabilidad
La existencia de ambas implementaciones demuestra que la arquitectura es blockchain-agnostic:
- EVM: Ideal para despliegue rápido en ecosistemas maduros
- Substrate: Ideal para blockchains específicas de propósito gubernamental
Esta portabilidad permite:
- Evaluación comparativa de costos y rendimiento
- Migración entre ecosistemas según necesidades institucionales
- Resiliencia ante cambios tecnológicos
Síntesis del Modelo
- Zero-Knowledge protege al votante (identidad off-chain)
- Blockchain protege al voto (inmutabilidad)
- El Smart Contract protege las reglas (ejecución automática)
- Los validadores protegen el proceso (gobernanza distribuida)
- La portabilidad protege contra obsolescencia tecnológica
El sistema no necesita confiar en personas, instituciones o intermediarios. Confía en criptografía, consenso y código inmutable. La legitimidad del voto no se declara: se demuestra criptográficamente.
Results
- Proyecto ganador en hackathon realizado en la Cámara de Diputados
- Validación del enfoque técnico en un entorno políticamente sensible
- Demostración funcional de voto inmutable y auditable
- Reconocimiento por el equilibrio entre seguridad, anonimato y transparencia
- Doble implementación (EVM + Substrate) demostrando portabilidad arquitectónica
- Smart Contracts desplegados y testeados durante las 30+ horas del evento
- Evaluación positiva sobre aspectos legales y vulnerabilidades físicas
Tech Stack
Blockchain Platforms
- EVM: Ethereum / Polygon / BSC compatible
- Substrate: Polkadot ecosystem ready
Smart Contracts
- Solidity: ^0.8.0 (EVM implementation)
- Rust: frame_support + sp_std (Substrate pallet)
Security & Privacy
- Zero-Knowledge Proofs: zk-SNARKs (conceptual layer)
- Cryptographic Primitives: Blake2, nullifiers, Merkle Trees
Architecture
- Layered Design: Off-chain identity + on-chain voting
- Multi-validator Governance: Distributed consensus mechanism
- Cross-chain Compatible: Blockchain-agnostic logic
Development
- Methodology: Rapid prototyping with production-grade security
- Focus: Social impact + institutional viability
Key Takeaways
La experiencia de más de 30 horas continuas no solo validó la arquitectura técnica, sino que expuso la complejidad de diseñar sistemas de votación que deben cumplir con:
- Requisitos legales (validez jurídica del voto electrónico)
- Seguridad física (protección de dispositivos y infraestructura)
- Confianza social (aceptación ciudadana e institucional)
- Escalabilidad técnica (procesamiento de millones de votos)
- Portabilidad tecnológica (independencia de plataformas específicas)
La doble implementación en Solidity y Rust demuestra que los principios arquitectónicos trascienden tecnologías específicas. El desafío real no es solo escribir código correcto, sino diseñar sistemas que la sociedad pueda confiar, independientemente de la blockchain subyacente.
Lecciones de Portabilidad
- El diseño conceptual sólido permite implementaciones en múltiples ecosistemas
- La separación de capas (zk off-chain + smart contract on-chain) es universal
- Los principios de gobernanza distribuida son independientes de la plataforma
- La arquitectura debe priorizar conceptos sobre tecnologías específicas