Você comprou um bilhete para uma loteria, onde o vencedor leva para casa $1.000.000. Quando a loteria é realizada, você descobre que ganhou e, os organizadores não querem pagar a você.
Além disso, imagine um sistema de loteria com 500 participantes, onde o vencedor pode ser previsto ou adulterado. Onde não há confiança entre os organizadores e os participantes. Você já se sentirá enganado quando não souber como o vencedor foi selecionado.
É aí que entra a blockchain, para remover o elemento de ter que confiar em um sistema antes de participar. O elemento de fraude ou falsificação.
Neste artigo, vou mostrar como garantir ainda mais a integridade em um sistema de loteria usando contratos inteligentes (Solidity e Chainlink VRF para obter um número aleatório).
Se você não souber como configurar um canal de assinatura no Chainlink VRF, tenho um artigo que fornece etapas detalhadas e pictóricas para criar uma assinatura.
Link -> https://shorturl.at/hwEG6.
Vamos mergulhar na implementação do sistema Chainlink VRF. Portanto, estaremos implementando nosso contrato inteligente com a Chainlink VRF na rede Goerli.
Acesse https://docs.chain.link/vrf/v2/subscription/supported-networks para saber mais sobre as redes suportadas pela Chainlink VRF.
Vamos precisar dos valores contidos na imagem acima quando começarmos a escrever nosso contrato inteligente.
Além disso, será necessário a rede de testes Goerli será necessária e pode ser obtida em https://goerlifaucet.com/
Vamos criar um arquivo lottery.sol e adicionar as variáveis básicas, eventos, e funções base.
//Uma variável de estado para os participantes
uint playerId;
//Uma variável de estado para a idade média de um participante
uint age = 18;
//Uma variável de estado para o status atual do nosso jogo de loteria
bool lotteryStatus;
//Uma variável de estado para saber se o jogo terminou
bool lotteryEnded;
//Uma variável de estado para o número máximo de participantes
uint constant MAX_PATICIPANTS = 100;
//Preço do bilhete de loteria
uint constant TICKET_PRICE = 1 ether;
//endereço do administrador
address lotteryAdmin;
//participantes da loteria
Participant[] participants;
struct Participant {
uint timeAdded;
uint lotteryCard;
address playerAddress;
}
//Evento para quando um participante é adicionado
event ParticipantAdded(address participant);
//Evento para o jogo inicia
event LotteryStarted(bool status, uint timeStart);
//Evento quando a solicitação ao VRF é feita
event RequestSent(uint256 requestId, uint32 numWords);
//Evento quando a solicitação ao VRF é atendida
event RequestFulfilled(uint256 requestId, uint256[] randomWords);
//Status da solicitação
struct RequestStatus {
bool fulfilled; // se a solicitação foi atendida com sucesso
bool exists; // se existe um requestId
uint256[] randomWords;
}
//VRF COORDINATOR baseado na rede que você irá implantar no
VRFCoordinatorV2Interface immutable COORDINATOR;
//id do seu canal de assinatura VRF
uint64 immutable s_subscriptionId;
//O valor do hash da chave da via de gás, que é o preço máximo do gás que você está disposto a pagar por uma solicitação em wei
bytes32 immutable s_keyHash;
//o limite de quanto gás usar para a solicitação de retorno de chamada para a função fillRandomWords() do seu contrato
uint32 constant CALLBACK_GAS_LIMIT = 2500000;
//Quantas vezes os valores aleatórios da sua solicitação serão confirmados pela
uint16 constant REQUEST_CONFIRMATIONS = 3;
//Quantos valores aleatórios você deseja do Chainlink. Two(2) é a
uint32 constant NUM_WORDS = 2;
// o último ID de solicitação que seu contrato recebeu do chainlink;
uint256 public lastRequestId;
// Construtor com todos os parâmetros necessários para
constructor() VRFConsumerBaseV2(0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D){
COORDINATOR = VRFCoordinatorV2Interface(0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D);
s_keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;
lotteryAdmin = msg.sender;
s_subscriptionId = //Você adicionará seu ID de assinatura;
}
Apenas estabelecemos os blocos de construção do nosso Sistema de Loteria. Vamos adicionar funcionalidade para adicionar participantes, iniciar e encerrar a Loteria.
// Iniciar loteria
function startLottery() external {
require(msg.sender == lotteryAdmin, "Unauthorized");
require(!lotteryStatus, "Lottery already active");
require(!lotteryEnded, "Lottery has ended");
require(participants.length > 10, "Particpants not enough to start");
lotteryStatus = true;
}
// Encerrar loteria
function endLottery() external {
require(msg.sender == lotteryAdmin, "Unauthorized");
require(lotteryStatus, "Lottery Game hasn't started");
require(!lotteryEnded, "Lottery already ended");
lotteryEnded = true;
playerId = 0;
}
Apenas adicionamos funcionalidade para permitir que apenas o administrador do jogo de loteria inicie e encerre a loteria. Também há uma verificação de que os participantes devem ter pelo menos 20 anos antes de o jogo começar.
Vamos adicionar a função que permite aos jogadores participarem na loteria.
function participate(uint _age) external payable {
//Cada jogador paga um preço de bilhete de 0,01ETH
require(msg.value == TICKET_PRICE, "Ticket Price is 0.01ETH");
//Verifica se cada jogador é maior de 18 anos
require(age >= _age, "You are not above 18Years");
//Verifica para confirmar que a loteria foi iniciada
require(lotteryStatus == false, "Lottery has started already");
//Verifica se o número máximo de jogadores não foi atingido
require(participants.length < 100, "Max Participants reached");
playerId++;
//Cria uma estrutura de detalhes do jogador e a adiciona à matriz de participantes
Participant memory _participant;
_participant.timeAdded = block.timestamp;
_participant.lotteryCard = playerId;
_participant.playerAddress = msg.sender;
participants.push(_participant);
//emite um evento informando que o jogador foi adicionado
emit ParticipantAdded(msg.sender);
}
Agora, como decidimos o processo para escolher um vencedor? Normalmente, poderíamos ter usado algum meio de aleatoriedade (usando block.timestamp, uma frase-semente e alguns valores, hasheado e depois realizar algumas operações aritméticas para selecionar um vencedor. Esse processo não é seguro e pode ser adulterado).
Aqui é onde entra o oráculo da Chainlink; a Chainlink fornece uma funcionalidade chamada Verifiable Random Function (Função aleatória verificável ou VRF).
A Chainlink VRF (Verifiable Random Function) é um gerador de números aleatórios (RNG) comprovadamente justo e verificável que permite que contratos inteligentes acessem valores aleatórios sem comprometer a segurança ou usabilidade.
A própria Chainlink fornece um código de contrato inteligente para solicitar e atender valores aleatórios a serem usados pelo seu contrato inteligente.
//Solicitação de valor aleatório
function requestRandomWords() external returns (uint256 requestId) {
requestId = COORDINATOR.requestRandomWords(
s_keyHash,
s_subscriptionId,
REQUEST_CONFIRMATIONS,
CALLBACK_GAS_LIMIT,
NUM_WORDS
);
s_requests[requestId] = RequestStatus({
randomWords: new uint256[](0),
exists: true,
fulfilled: false
});
lastRequestId = requestId;
emit RequestSent(requestId, NUM_WORDS);
return requestId;
}
// Atender à solicitação de aleatoriedade do elo da corrente
function fulfillRandomWords(
uint256 _requestId,
uint256[] memory _randomWords
) internal override {
require(s_requests[_requestId].exists, "request not found");
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
emit RequestFulfilled(_requestId, _randomWords);
}
Cada linha de código no trecho acima trabalha com variáveis que expliquei quando estabelecemos a base para o nosso contrato. A função requestRandomWords
recebe os parâmetros especificados e envia a solicitação para o contrato do coordenador VRF.
Quando a função requestRandomWords
é chamada, um valor de ID de solicitação é retornado da ChainLink VRF. Este é um exemplo de saída quando usei o Remix para testar o jogo de loteria.
Nos logs de saída, o ID da solicitação é 65372568078059010733545628340548094994590515889458705464888828779780316747169.
A função fulfilledRandomWords
, que é interna, recebe os valores aleatórios e os armazena em seu contrato. Temos uma função que obtém os valores aleatórios conectados a um ID de solicitação.
function getRequestStatus(uint256 _requestId) external view returns (bool fulfilled, uint256[] memory randomWords) {
require(s_requests[_requestId].exists, "solicitação não encontrada");
RequestStatus memory request = s_requests[_requestId];
return (request.fulfilled, request.randomWords);
}
A função getRequestStatus
simplesmente mostra as palavras aleatórias solicitadas depois que foram atendidas. A saída decodificada consiste no estado de nossa solicitação, o ID e, em seguida, o mais importante, os valores gerados aleatoriamente pela Chainlink VRF.
Este valor será usado para gerar nosso vencedor aleatório para o jogo de loteria.
Agora, vamos escrever uma função que escolhe um vencedor para a loteria.
function pickWinner() external payable returns (Participant memory winner) {
require(!lotteryStatus, "Lottery Game not active");
require(msg.sender == lotteryAdmin, "Unauthorized Access");
RequestStatus memory _request = s_requests[lastRequestId];
uint randomWinner = _request.randomWords[0] % participants.length;
winner = participants[randomWinner];
}
Esta é uma função muito simples que utiliza o poder da Chainlink VRF; nosso vencedor não pode ser determinado por qualquer violação de segurança, pois há uma verificação rigorosa pela Chainlink VRF nos bastidores para confirmar a singularidade de nossos valores aleatórios.
Quando o administrador chama a função pickWinner
, obtemos uma saída decodificada, como o trecho abaixo.
A partir disso, podemos ver que o vencedor é um usuário com o bilhete de loteria nº 2.
Conseguimos criar um contrato que simula um jogo de loteria da vida real, onde os jogadores são adicionados e um vencedor aleatório é selecionado usando a Chainlink VRF. Dessa forma, eliminamos a necessidade de qualquer verificação para evitar violações externas ao nosso jogo de loteria.
Por favor, aplauda, comente e siga para mais artigos como este sobre conceitos de blockchain, escrita de contratos inteligentes e Web3.
Se você achar isso esclarecedor e útil, pode me comprar um café em https://www.buymeacoffee.com/etimpaul22a.
Artigo escrito por Joe. Traduzido por Marcelo Panegali.
Latest comments (0)