SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
02.12.2024

Aula 204: Construindo Aleatoriedade Segura em Solidity

No mundo das aplicações descentralizadas (dApps), especialmente em jogos e loterias baseados em blockchain, a aleatoriedade segura é fundamental. A geração de números aleatórios previsíveis pode levar a exploração e manipulação, resultando em uma perda de confiança na aplicação. Nesta aula, vamos explorar como construir aleatoriedade segura em Solidity.

Por que a Aleatoriedade é Importante?

A aleatoriedade desempenha um papel crucial em várias aplicações, como:

  • Jogos: Determinando o resultado de um lançamento de dados ou uma distribuição de cartas.
  • Loterias: Selecionando um vencedor de um grupo de participantes.
  • NFTs: Gerando atributos únicos para ativos digitais.

No entanto, obter aleatoriedade segura é um desafio devido à natureza transparente e determinística das redes blockchain. Podemos aproveitar fontes externas e métodos validados pela comunidade para alcançar aleatoriedade segura.

Métodos para Aleatoriedade Segura

1. Variáveis de Bloco On-Chain

Usar variáveis de bloco (como block.number, block.timestamp, etc.) é um método comum, mas inseguro, para aleatoriedade. Embora você possa combiná-las para gerar um número pseudo-aleatório, um minerador malicioso pode manipular o resultado.

Exemplo: Aleatoriedade Insegura usando Variáveis de Bloco

pragma solidity ^0.8.0;

contract AleatoriedadeInsegura {
    function obterNumeroAleatorio() public view returns (uint) {
        // Combinando variáveis de bloco para aleatoriedade
        return uint(keccak256(abi.encodePacked(block.timestamp, block.difficulty, block.number)));
    }
}

Problemas:

  • Previsível por mineradores.
  • Fácil de manipular por atores maliciosos.

2. Oráculos

Para construir aplicações seguras, usar oráculos, como o Chainlink VRF (Função Aleatória Verificável), é uma abordagem preferida. Um oráculo é um serviço off-chain que fornece dados externos a contratos inteligentes.

Exemplo: Aleatoriedade Segura usando Chainlink VRF

pragma solidity ^0.8.0;

import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";

contract AleatoriedadeSegura is VRFConsumerBase {
    bytes32 internal keyHash;
    uint256 internal fee;
    uint256 public resultadoAleatorio;

    constructor(address _vrfCoordinator, address _linkToken, bytes32 _keyHash) 
        VRFConsumerBase(_vrfCoordinator, _linkToken) 
    {
        keyHash = _keyHash;
        fee = 0.1 * 10 ** 18; // 0.1 LINK
    }

    // Solicitar número aleatório seguro
    function obterNumeroAleatorio() public returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) >= fee, "Não há LINK suficiente");
        return requestRandomness(keyHash, fee);
    }

    // Função de callback usada pelo Coordenador VRF
    function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        resultadoAleatorio = randomness;
    }
}

Como funciona:

  • Este contrato estende VRFConsumerBase.
  • Solicita aleatoriedade do serviço Chainlink VRF e a recupera de forma segura.
  • A função de callback fulfillRandomness recebe o valor aleatório.

3. Esquema de Compromisso e Revelação

Outro método para obter aleatoriedade segura é usar um esquema de compromisso e revelação. Os participantes enviam um valor hashed de sua escolha aleatória sem revelá-la inicialmente. Uma vez que todos os valores são enviados, eles revelam suas escolhas, e um número aleatório pode ser computado.

Exemplo: Esquema de Compromisso e Revelação

pragma solidity ^0.8.0;

contract CompromissoRevelacao {
    struct Participante {
        bytes32 compromisso;
        uint256 numeroRevelado;
    }

    mapping(address => Participante) public participantes;
    address[] public enderecosParticipantes;

    function comprometer(bytes32 _compromisso) public {
        participantes[msg.sender].compromisso = _compromisso;
        enderecosParticipantes.push(msg.sender);
    }

    function revelar(uint256 _numero) public {
        Participante storage participante = participantes[msg.sender];
        require(participante.compromisso != bytes32(0), "Você deve se comprometer primeiro.");

        // Verificar se o compromisso corresponde ao número revelado
        require(participante.compromisso == keccak256(abi.encodePacked(_numero)), "Revelação inválida.");

        participante.numeroRevelado = _numero;
    }

    function obterNumeroAleatorio() public view returns (uint256) {
        uint256 total = 0;
        for (uint256 i = 0; i < enderecosParticipantes.length; i++) {
            total += participantes[enderecosParticipantes[i]].numeroRevelado;
        }

        return uint256(keccak256(abi.encodePacked(total))) % 100; // Número aleatório [0, 99]
    }
}

Como funciona:

  • Os participantes se comprometem com suas escolhas aleatórias usando o hash.
  • Após todas as compromissos serem coletados, os participantes revelam seus números.
  • O contrato então calcula um número aleatório usando os valores revelados.

Conclusão

A aleatoriedade segura é vital para manter a integridade de várias aplicações na blockchain. Desde a utilização de oráculos externos como o Chainlink VRF até a implementação de esquemas de compromisso e revelação, os desenvolvedores devem ser cautelosos ao projetar soluções de aleatoriedade. Sempre avalie as implicações de segurança dos métodos que você escolhe para garantir confiança e justiça em suas dApps.

Video

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

Thank you for voting!