SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
18.11.2024

Aula 062: Máquinas de Estado em Programas Solana

Máquinas de estado são um conceito fundamental na programação que ajudam a gerenciar a transição de estados em um programa. No contexto dos programas Solana, usar máquinas de estado pode gerenciar efetivamente transições de estado e lógicas complexas. Esta aula irá guiá-lo na criação de um programa básico em Solana com uma implementação de máquina de estado.

Compreendendo Máquinas de Estado

Uma máquina de estado consiste em estados, transições entre esses estados e ações que podem ser executadas com base no estado atual. Em um programa Solana, o design da máquina de estado pode ajudar em cenários como o tratamento de interações do usuário, gerenciamento de tokens ou controle do fluxo de um processo em várias etapas.

Implementando uma Máquina de Estado em Solana

Vamos criar um exemplo simples de um sistema de votação que permite que os usuários votem em propostas. O sistema de votação incluirá três estados:

  1. Inicializado: A votação foi configurada, mas não iniciada.
  2. Votação: A votação está aberta e os usuários podem registrar seus votos.
  3. Concluído: A votação foi encerrada e os resultados foram contabilizados.

Configurando Seu Programa Solana

Primeiro, certifique-se de que você tenha a ferramenta Solana configurada. Crie um novo programa Solana usando o anchor:

anchor init programa_votacao
cd programa_votacao

Definindo o Estado do Programa

No arquivo lib.rs do seu programa, vamos definir os estados e a estrutura para nosso processo de votação.

use anchor_lang::prelude::*;

declare_id!("SeuIdDoProgramaAqui");

#[program]
pub mod programa_votacao {
    use super::*;

    pub fn inicializar(ctx: Context<Inicializar>, nome_proposta: String) -> Result<()> {
        let conta_votacao = &mut ctx.accounts.conta_votacao;
        conta_votacao.estado = EstadoVotacao::Inicializado;
        conta_votacao.nome_proposta = nome_proposta;
        Ok(())
    }

    pub fn iniciar_votacao(ctx: ContextIniciarVotacao) -> Result<()> {
        let conta_votacao = &mut ctx.accounts.conta_votacao;
        require!(conta_votacao.estado == EstadoVotacao::Inicializado, ErroPersonalizado::EstadoInvalido);
        conta_votacao.estado = EstadoVotacao::Votacao;
        Ok(())
    }

    pub fn votar(ctx: Context<Votar>, escolha: bool) -> Result<()> {
        let conta_votacao = &mut ctx.accounts.conta_votacao;
        require!(conta_votacao.estado == EstadoVotacao::Votacao, ErroPersonalizado::EstadoInvalido);
        // Lógica para contar os votos vai aqui
        // ...
        Ok(())
    }

    pub fn encerrar_votacao(ctx: Context<EncerrarVotacao>) -> Result<()> {
        let conta_votacao = &mut ctx.accounts.conta_votacao;
        require!(conta_votacao.estado == EstadoVotacao::Votacao, ErroPersonalizado::EstadoInvalido);
        conta_votacao.estado = EstadoVotacao::Concluido;
        Ok(())
    }
}

#[account]
pub struct ContaVotacao {
    pub estado: EstadoVotacao,
    pub nome_proposta: String,
}

#[derive(PartialEq, Clone, AnchorSerialize, AnchorDeserialize)]
pub enum EstadoVotacao {
    Inicializado,
    Votacao,
    Concluido,
}

#[error]
pub enum ErroPersonalizado {
    EstadoInvalido,
}

Contextos para Cada Instrução

Agora, vamos definir os contextos para cada uma das instruções do nosso programa. Isso garante que as contas necessárias para cada operação sejam fornecidas e sejam válidas.

#[derive(Accounts)]
pub struct Inicializar<'info> {
    #[account(init, payer = usuario, space = 8 + std::mem::size_of::<ContaVotacao>())]
    pub conta_votacao: Account<'info, ContaVotacao>,
    #[account(mut)]
    pub usuario: Signer<'info>,
    pub sistema_programa: Program<'info, System>,
}

#[derive(Accounts)]
pub struct IniciarVotacao<'info> {
    #[account(mut)]
    pub conta_votacao: Account<'info, ContaVotacao>,
}

#[derive(Accounts)]
pub struct Votar<'info> {
    #[account(mut)]
    pub conta_votacao: Account<'info, ContaVotacao>,
    pub usuario: Signer<'info>,
}

#[derive(Accounts)]
pub struct EncerrarVotacao<'info> {
    #[account(mut)]
    pub conta_votacao: Account<'info, ContaVotacao>,
}

Construindo e Testando

Uma vez que tudo esteja configurado, construa seu programa:

anchor build

Para testar seu programa, crie um cenário de teste no diretório tests, onde você pode simular os diferentes estados e transições.

#[cfg(test)]
mod testes {
    use super::*;
    use anchor_lang::prelude::*;
    use anchor_lang::solana_program::pubkey::Pubkey;

    #[test]
    fn teste_maquina_estado_votacao() {
        let programa = Program::new();
        let (conta_votacao, _) = Pubkey::find_program_address(&[b"chave_votacao"], &programa.id());

        // Testar inicialização
        let inicializado = programa.inicializar(conta_votacao, "Proposta A").unwrap();
        assert_eq!(inicializado.estado, EstadoVotacao::Inicializado);

        // Testar início da votação
        let iniciado = programa.iniciar_votacao(conta_votacao).unwrap();
        assert_eq!(iniciado.estado, EstadoVotacao::Votacao);

        // Testar votação
        programa.votar(conta_votacao, true).unwrap();

        // Testar encerramento da votação
        let concluido = programa.encerrar_votacao(conta_votacao).unwrap();
        assert_eq!(concluido.estado, EstadoVotacao::Concluido);
    }
}

Conclusão

Nesta aula, implementamos uma máquina de estado básica para um programa de votação na Solana. Ao definir estados, transições e instruções associadas, criamos uma estrutura funcional para gerenciar transições de estado em um programa Solana. À medida que você continua a desenvolver aplicações mais complexas, considerar máquinas de estado o ajudará a gerenciar o fluxo da aplicação de forma eficaz.

Did you like this article? Rate it from 1 to 5:

Thank you for voting!