Aleatório baseado em blockchain RSA

Há um problema que precisamos resolver no desenvolvimento de nossos jogos. É complicado gerar um número aleatório em uma rede distribuída. Quase todas as blockchains já enfrentaram esse problema. De fato, em redes onde não há confiança entre ninguém, a criação de um número aleatório resolve uma ampla gama de problemas.

Neste artigo, explicamos como resolvemos esse problema para nossos jogos. A primeira delas foi a Waves Xmas Tree .





Inicialmente, planejamos gerar um número usando informações da blockchain. No entanto, em uma investigação mais aprofundada, ficou claro que o processo usado para criar um número dessa maneira poderia ser manipulado. Tivemos que descartar essa solução.

Criamos uma solução alternativa, usando um esquema de 'confirmação de revelação'. O servidor propôs um número de 1 a 5, adicionou 'salt' a ele e hash o resultado usando a função Keccak . O servidor pré-depurou um contrato inteligente com um número já salvo. O resultado foi que o jogo foi efetivamente reduzido ao usuário adivinhando o número oculto pelo hash.

O jogador fez sua aposta e o servidor enviou um número oculto e 'salt' para um contrato inteligente. Em outras palavras, as cartas foram reveladas. Depois, o servidor verificou os números e decidiu se o usuário havia ganho ou perdido.

Se o servidor não enviou o número e 'salt' para verificação, o usuário venceu. Nesse caso, era necessário implantar um contrato inteligente com antecedência e organizar possíveis ganhos para cada jogo. Isso foi inconveniente, caro e demorado. Naquela época, porém, não havia outra solução segura.

Logo depois, a equipe da Tradisys propôs adicionar a função rsaVerify () ao protocolo Waves. Isso verifica a validade de uma assinatura RSA com base em chaves públicas e privadas. Como resultado da nossa proposta, a função foi adicionada.

Criamos três novos jogos: Dice Roller , Coin Flip e Ride On Waves . Em cada um deles, a nova tecnologia de números aleatórios foi implementada. Vamos dar uma olhada em como funciona.



Vejamos primeiro a geração de números aleatórios. Você pode encontrar o contrato inteligente aqui .

Vá para a guia Script e escolha Descompilado . Você verá o código (ou script) do contrato inteligente.



O código do contrato inteligente consiste em uma lista de funções. Os que são @Callable podem ser executados através de transações de Invocação . Estamos interessados ​​em dois deles: apostar e retirar :
  • func bet (playerChoice)
  • retirada de função (gameId, rsaSign)


1. O usuário escolhe o alcance e o tamanho da aposta.



2. O cliente organiza a função de aposta. Para a imagem acima, seria apostado ("50")

3. O cliente envia uma transação de invocação 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 no contrato inteligente (escolha: String).



4. Vejamos 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 registra um novo jogo no estado do contrato inteligente:

  • Novo ID do jogo exclusivo (ID do jogo)
  • Estado do jogo = ENVIADO
  • Escolha do jogador (o intervalo é 50)
  • Chave pública
  • Recompensa potencial (depende da aposta do jogador)




É assim que o banco de dados de valores-chave fica na blockchain:

 { "type": "string", "value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229", "key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx" } 


'Key' é o ID do jogo para um novo jogo. Os dados restantes estão contidos no campo 'valor'. Essas entradas são armazenadas na guia Dados do contrato inteligente:




5. O servidor encontra a transação enviada (o novo jogo) via API do blockchain. O ID do jogo já está registrado na blockchain, por isso é impossível alterá-lo ou excluí-lo.

6. O servidor forma uma função de retirada (gameId, rsaSign), como:

retirar ( «FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq», «base64: Gy69dKdmXUEsAmUrpoWxDLTQOGj5 / qO8COA + QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL / TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk / GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY / 0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr / ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa / QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr ​​/ q0x6Bg ==»)

7. O servidor envia uma 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 e uma assinatura RSA de um ID exclusivo. O resultado da assinatura é imutável.

O que isso significa?


Pegamos o mesmo valor (id do jogo) e aplicamos o método de assinatura RSA a ele. É assim que o algoritmo RSA funciona. É impossível manipular o número final porque o ID do jogo e o resultado do algoritmo RSA são desconhecidos. Também não faz sentido tentar adivinhar um número.

8. O blockchain recebe uma transação que executa a função de retirada (gameId, rsaSign).

9. Há uma chamada para a função GenerateRandIn dentro da função de retirada (gameId, rsaSign). 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 é um número aleatório

Primeiramente, a string resultante da assinatura RSA é obtida. Em seguida, é feito o hash via SHA-256 ( sha256 (rsaSign) ).

Não podemos prever o resultado da assinatura e o hash subsequente. Assim, é impossível afetar sua geração. Para obter um número em um intervalo específico (por exemplo, de 1 a 100), as funções de conversão toInt e% 100 ( mod analógico) são aplicadas.

No início do artigo, mencionamos a função rsaVerify () que permite verificar a validade de uma assinatura RSA por uma chave privada contra uma pública. Aqui está uma parte do GenerateRandInt (gameId, rsaSign):

rsaVerify (SHA256, toBytes (gameId), rsaSign, RSAPUBLIC)

Para começar, a chave pública RSAPUBLIC e a sequência rsaSign são obtidas. A assinatura é verificada quanto à validade. Se a verificação for bem sucedida, o número é gerado. Caso contrário, o sistema considera que a assinatura não é válida (assinatura RSA inválida).

O servidor precisa assinar o ID do jogo usando uma chave privada e enviar uma assinatura RSA válida dentro de 2.880 blocos. A opção é gerenciada enquanto o contrato inteligente está sendo implantado. Se nada acontecer no tempo determinado, o usuário vence. Nesse caso, a recompensa deve ser enviada pelo usuário de forma independente. Acontece que a trapaça não é lucrativa para o servidor, pois isso leva a uma perda. Há um exemplo abaixo.



O usuário joga Dice Roller . Ele escolhe 2 de 6 faces de cubo, com uma aposta de 14 ONDAS. Se o servidor não enviar uma assinatura RSA válida para o contrato inteligente dentro de um tempo definido (2.880 blocos), o usuário receberá 34,44 WAVES.

Para a geração de números, usamos um oráculo, um sistema externo e não o blockchain. O servidor implementa uma assinatura RSA para o ID do jogo. O contrato inteligente verifica a validade da assinatura e determina o vencedor. Se o servidor não enviar nada, o usuário vencerá automaticamente.

Este método garante que a manipulação seja tecnicamente impossível. Todos os jogos da Tradisys são baseados no algoritmo descrito acima - garantindo que nossos jogos sejam justos e transparentes. Tudo pode ser auditado publicamente para garantir a honestidade.

Source: https://habr.com/ru/post/pt464395/


All Articles