Há um problema - é difícil gerar um número aleatório em uma rede descentralizada. Quase todas as blockchains já encontraram isso. De fato, em redes onde não há confiança entre usuários, a criação de um número aleatório inegável resolve muitos problemas.
O artigo explica como resolver o problema usando jogos como exemplo. A primeira delas foi a
Waves Xmas Tree . Para o desenvolvimento, precisávamos de um gerador de números aleatórios.

Inicialmente, planejamos gerar um número com base nas informações do blockchain. No entanto, ficou claro: o número poderia ser fraudado, o que significa que a solução não se encaixa.
Chegamos a uma solução alternativa: use o esquema de divulgação e confirmação. O servidor "adivinhou" um número de 1 a 5, adicionou "salt" a ele e, em seguida, hash o resultado usando
a função Keccak . O servidor pré-implantou um contrato inteligente com um número já salvo. Acontece que o jogo foi reduzido ao fato de o usuário adivinhar o número oculto pelo hash.
O jogador fez uma aposta e o servidor enviou o número solicitado e "salt" para o contrato inteligente. Em linguagem clara, ele revelou os cartões. Depois disso, o servidor verificou os números e decidiu se o usuário venceria ou perderia.
Se o servidor não enviou um número ou "salt" para verificação, o usuário venceu. Nesse caso, para cada jogo era necessário implantar um contrato inteligente com antecedência e estabelecer possíveis ganhos nele. Acabou sendo inconveniente, longo e caro. Naquela época, não havia outra solução segura.
Recentemente, a equipe da Tradisys propôs adicionar a função
rsaVerify () ao protocolo Waves. Ele verifica a validade da assinatura RSA com base nas chaves pública e privada. Como resultado, a função foi adicionada.
Desenvolvemos três jogos:
Dice Roller ,
Coin Flip e
Ride On Waves . Cada tecnologia implementada de números aleatórios. Vamos ver como isso funciona.

Considere a geração aleatória de números usando o Ride on Waves como exemplo. Um contrato inteligente pode ser encontrado
aqui .
Vá para a guia
Script e selecione
Descompilado . Veja o código do contrato inteligente (também conhecido como script).

O código do contrato inteligente contém um conjunto de funções. Os marcados como @Callable podem ser acionados usando
transações de Invocação . Estamos interessados em duas funções:
apostar e
sacar :
- func bet (playerChoice)
- retirada de função (gameId, rsaSign)
1. O usuário seleciona a duração do segmento e o tamanho da aposta.

2. O cliente forma uma função de aposta. Para a imagem acima, isso será
apostado ("50") .
3. O cliente envia a transação de chamada para o endereço do contrato inteligente (broadcast InvocationTx). Uma transação como um parâmetro de chamada contém a função de aposta. Isso significa que a transação de invocação inicia a execução da função de aposta (escolha: String) no contrato inteligente.

4. Considere a função de aposta:
@Callable(i) func bet (playerChoice) = { let newGameNum = IncrementGameNum() let gameId = toBase58String(i.transactionId) let pmt = extract(i.payment) let betNotInWaves = isDefined(pmt.assetId) let feeNotInWaves = isDefined(pmt.assetId) let winAmt = ValidateBetAndDefineWinAmt(pmt.amount, playerChoice) let txIdUsed = isDefined(getString(this, gameId)) if (betNotInWaves) then throw ("Bet amount must be in Waves") else if (feeNotInWaves) then throw ("Transaction's fee must be in Waves") else if (txIdUsed) then throw ("Passed txId had been used before. Game aborted.") else { let playerPubKey58 = toBase58String(i.callerPublicKey) let gameDataStr = FormatGameDataStr(STATESUBMITTED, playerChoice, playerPubKey58, height, winAmt, "") ScriptResult(WriteSet(cons(DataEntry(RESERVATIONKEY, ValidateAndIncreaseReservedAmt(winAmt)), cons(DataEntry(GAMESCOUNTERKEY, newGameNum), cons(DataEntry(gameId, gameDataStr), nil)))), TransferSet(cons(ScriptTransfer(SERVER, COMMISSION, unit), nil))) } }
A função grava um novo jogo no estado do contrato inteligente. Ou seja:
- Identificador exclusivo para um novo jogo (ID do jogo)
- Estado do jogo = ENVIADO
- Seleção de jogador (comprimento da linha 50)
- Chave pública
- Potenciais ganhos (depende da aposta do jogador)

É assim que o registro de dados na blockchain se parece (valor-chave):
{ "type": "string", "value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229", "key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx" }
"Key" (chave) -
ID do jogo. Os dados restantes estão contidos na linha do campo "valor" (valor). Essas entradas são armazenadas na guia
Dados do contrato inteligente:


5. O servidor "examina" o contrato inteligente e encontra a transação enviada (novo jogo) usando a API do blockchain. O código do jogo já está registrado na blockchain, o que significa que você não pode alterá-lo ou influenciá-lo
6. O servidor forma uma função de retirada (gameId, rsaSign). Por exemplo, isto:
withdraw ("FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq", "base64:Gy69dKdmXUEsAmUrpoWxDLTQOGj5/qO8COA+QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL/TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk/GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY/0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr/ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa/QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr/q0x6Bg==")
7. O servidor envia a transação de chamada para o contrato inteligente (broadcast InvocationTx). A transação contém uma chamada para a função de retirada gerada (gameId, rsaSign):

A função contém o
ID do jogo novo e o resultado da assinatura RSA do identificador exclusivo com uma chave privada. O resultado da assinatura é inalterado.
O que isso significa?Pegamos o mesmo valor (id do jogo) e aplicamos o método de assinatura RSA a ele. Sempre obteremos o mesmo resultado. É assim que o algoritmo RSA funciona. Você não pode manipular o número final, pois o ID do jogo e o resultado do uso do RSA não são conhecidos. Combinar um número também não faz sentido.
8. O blockchain aceita a transação. Ele executa a função de retirada (gameId, rsaSign)
9. Dentro da função de retirada, a
função GenerateRandInt (gameId, rsaSign) é exportada. Este é um gerador de números aleatórios
# @return 1 ... 100 func GenerateRandInt (gameId,rsaSign) = { # verify RSA signature to proof random let rsaSigValid = rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC) if (rsaSigValid) then { let rand = (toInt(sha256(rsaSign)) % 100) if ((0 > rand)) then ((-1 * rand) + 1) else (rand + 1) } else throw ("Invalid RSA signature") }
rand - e há um número aleatório.
Primeiro, uma string é obtida, que é o resultado da assinatura RSA do
ID do
jogo com a chave privada (
rsaSign ). Em seguida, é feito o hash usando SHA-256 (
sha256 (rsaSign) ).
Não podemos prever o resultado da assinatura e do hash subsequente. Portanto, é impossível influenciar a geração de um número aleatório. Para obter um número em um determinado intervalo (por exemplo, de 1 a 100), as funções de conversão toInt e% 100 são usadas (analógico para
mod ).
No início do artigo, mencionamos a função
rsaVerify () , que permite verificar a validade das assinaturas RSA com uma chave privada usando uma chave pública. Aqui está a parte GenerateRandInt (gameId, rsaSign):
rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)
A chave pública RSAPUBLIC e a sequência rsaSign são passadas para a entrada. A assinatura é verificada quanto à validade. Um número é gerado se a verificação for bem-sucedida. Caso contrário, o sistema considera que a assinatura não é válida (assinatura RSA inválida).
O servidor deve assinar o ID do jogo com a chave privada e enviar uma assinatura Rsa válida dentro de 2880 blocos. O parâmetro é configurado ao implantar um contrato inteligente. Se nada acontecer durante o tempo alocado, o usuário vence. Nesse caso, o prêmio deve ser enviado para o seu endereço. Acontece que o servidor "não é lucrativo para trapacear", porque isso leva a uma perda. Abaixo está um exemplo.

Usuário joga
Dice Roller . Eu escolhi 2 das 6 faces do cubo, a aposta é de 14 ONDAS. Se o servidor não enviar uma assinatura RSA válida no contrato inteligente dentro do tempo definido (2880 blocos), o usuário utilizará 34,44 WAVES.
Para gerar números nos jogos, usamos o oracle - um sistema externo e sem bloqueio. O servidor implementa a assinatura RSA do ID do jogo. O contrato inteligente verifica a validade da assinatura e determina o vencedor. Se o servidor não enviou nada, o usuário vence automaticamente.
O método descrito garante que a manipulação seja tecnicamente impossível. Todos os jogos da Tradisys usam um algoritmo, portanto, são honestos e transparentes. Tudo se presta ao escrutínio público. Isso garante honestidade.