Aula 038: Implementando Sistemas de Votação On-Chain
Nesta aula, vamos explorar como implementar um sistema de votação on-chain usando Rust e a blockchain Solana. Sistemas de votação são cruciais para aplicações descentralizadas (dApps), pois permitem que os participantes tomem decisões de maneira confiável. Este tutorial oferecerá uma implementação simples, guiando você na criação de um contrato de votação que permite aos usuários criar enquetes e votar nelas.
Configurando Seu Ambiente
Antes de começarmos, certifique-se de que você tenha o seguinte configurado:
-
Rust e Cargo: Garanta que Rust e Cargo estejam instalados em sua máquina. Você pode instalá-los no site oficial do Rust.
-
Solana CLI: Instale as ferramentas de linha de comando da Solana seguindo as instruções de instalação no site oficial da Solana.
-
Anchor Framework: Usaremos o framework Anchor para simplificar o desenvolvimento de contratos inteligentes na Solana. Instale-o utilizando:
cargo install --git https://github.com/coral-xyz/anchor.git --tag v0.26.0 anchor-cli --locked
-
Crie um Novo Projeto: Inicie um novo projeto Anchor executando:
anchor init sistema_de_votacao_on_chain cd sistema_de_votacao_on_chain
Entendendo o Design do Sistema de Votação
Nosso sistema de votação permitirá que os usuários:
- Criem uma enquete com uma pergunta única e múltiplas opções.
- Votem em uma opção específica da enquete.
- Recuperem os resultados da enquete.
Estruturas de Dados
Definiremos duas estruturas de dados: Enquete
e Voto
.
use anchor_lang::prelude::*;
#[account]
pub struct Enquete {
pub pergunta: String,
pub opcoes: Vec<String>,
pub votos: Vec<u32>,
pub proprietario: Pubkey,
}
#[account]
pub struct Voto {
pub eleitor: Pubkey,
pub id_enquete: Pubkey,
pub index_opcao: usize,
}
Neste exemplo:
Enquete
contém a pergunta, as opções possíveis, o número de votos para cada opção e a chave pública do proprietário.Voto
registra quem votou, em qual enquete e qual opção foi selecionada.
Implementando a Criação da Enquete
Vamos implementar a função para criar uma enquete:
#[program]
mod sistema_de_votacao_on_chain {
use super::*;
pub fn criar_enquete(ctx: Context<CriarEnquete>, pergunta: String, opcoes: Vec<String>) -> Result<()> {
let enquete = &mut ctx.accounts.enquete;
enquete.pergunta = pergunta;
enquete.opcoes = opcoes.clone();
enquete.votos = vec![0; opcoes.len()]; // Inicializa os votos para cada opção
enquete.proprietario = *ctx.accounts.usuario.key;
Ok(())
}
}
#[derive(Accounts)]
pub struct CriarEnquete<'info> {
#[account(init, payer = usuario, space = 8 + 64 + 4 * 32)]
pub enquete: Account<'info, Enquete>,
#[account(mut)]
pub usuario: Signer<'info>,
pub system_program: Program<'info, System>,
}
Explicação
- A função
criar_enquete
inicializa uma nova conta deEnquete
com a pergunta e as opções fornecidas. - O vetor
votos
é inicializado com zero para cada opção. - A estrutura
CriarEnquete
gerencia as contas envolvidas na transação.
Implementando a Funcionalidade de Votação
Agora, vamos implementar a função que permite que os usuários votem:
pub fn registrar_voto(ctx: Context<RegistrarVoto>, index_opcao: usize) -> Result<()> {
let enquete = &mut ctx.accounts.enquete;
let eleitor = &ctx.accounts.eleitor;
require!(index_opcao < enquete.opcoes.len(), CustomError::EscolhaInvalida);
enquete.votos[index_opcao] += 1;
let voto = &mut ctx.accounts.voto;
voto.eleitor = *eleitor.key;
voto.id_enquete = *enquete.key;
voto.index_opcao = index_opcao;
Ok(())
}
#[derive(Accounts)]
pub struct RegistrarVoto<'info> {
#[account(mut)]
pub enquete: Account<'info, Enquete>,
#[account(init, payer = eleitor, space = 8 + 8 + 32 + 4)]
pub voto: Account<'info, Voto>,
#[account(mut)]
pub eleitor: Signer<'info>,
pub system_program: Program<'info, System>,
}
Explicação
- A função
registrar_voto
atualiza a contagem de votos para a opção selecionada e armazena a escolha do eleitor. - Verificamos se o índice da opção é válido e gerenciamos as contas por meio da estrutura
RegistrarVoto
.
Recuperando os Resultados da Enquete
Por fim, vamos implementar uma função para recuperar os resultados atuais de uma enquete:
pub fn obter_resultados_enquete(ctx: Context<ObterResultadosEnquete>) -> Result<ResultadosEnquete> {
let enquete = &ctx.accounts.enquete;
Ok(ResultadosEnquete {
pergunta: enquete.pergunta.clone(),
opcoes: enquete.opcoes.clone(),
votos: enquete.votos.clone(),
})
}
#[derive(Accounts)]
pub struct ObterResultadosEnquete<'info> {
pub enquete: Account<'info, Enquete>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct ResultadosEnquete {
pub pergunta: String,
pub opcoes: Vec<String>,
pub votos: Vec<u32>,
}
Explicação
- A função
obter_resultados_enquete
permite que os usuários consultem o estado atual da enquete. - Ela retorna a pergunta da enquete, as opções disponíveis e o número de votos por opção.
Conclusão
Parabéns! Você implementou um sistema básico de votação on-chain usando Rust na blockchain Solana. Agora você pode criar enquetes, registrar votos e recuperar resultados, tudo armazenado de forma segura on-chain.
Em aulas futuras, considere adicionar recursos como:
- Datas de expiração para enquetes.
- Autenticação de usuários para evitar votos duplicados.
- Interface aprimorada para uma melhor interação.
Isso fornece uma base sólida para sua aplicação de votação descentralizada. Boa programação!