Descubra a Cloudbet.com
certified checkmark stamp next to cloudbet logo

Como funciona o jogo provably fair “Mines” da Cloudbet: uma análise técnica


Os jogos de apostas provably fair resolvem um problema que é inerente aos cassinos online desde sua criação: como um jogador pode ter certeza de que o resultado de um jogo não foi manipulado posteriormente? A resposta, ao que parece, é a mesma ferramenta que os criptógrafos utilizam sempre que duas partes precisam chegar a um acordo sobre algo sem confiar uma na outra, um esquema de “compromisso-revelação”.

A seguir, apresentamos um passo a passo completo do fluxo de dados que a Cloudbet utiliza para seu jogo Mines, desde a geração da seed até o posicionamento das minas, e como qualquer jogador com um pouco de conhecimento em programação pode verificar de forma independente todos os resultados de todas as partidas que já jogou.

Este artigo é o primeiro de uma série planejada de análises técnicas sobre os mecanismos provably fair por trás do Cloudbet Originals. Para verificar pessoalmente qualquer rodada (incluindo o jogo “Mines”), confira a Calculadora Provably Fair da Cloudbet.

Comprometer-se com o resultado antes do início da partida

Antes de cada rodada, o servidor gera uma seed do servidor aleatória de 32 bytes e publica imediatamente seu hash SHA3-256 – o compromisso. Esse hash é uma impressão digital unidirecional: ele comprova que a seed existia naquele momento, sem revelar qual era ela.

Assim que o jogo termina, a Cloudbet divulga a seed bruta do servidor. Qualquer pessoa pode calcular o hash e confirmar se ele corresponde ao compromisso assumido antes do jogo, o que prova que a semente e, portanto, o layout da mina, foram definidos antes que a primeira casa fosse escolhida. Não há nenhuma oportunidade em que o cassino pudesse trocá-lo por um valor mais vantajoso.

Incorporando a entropia dos jogadores

Apenas a seed do servidor deixaria os jogadores totalmente dependentes da aleatoriedade do cassino. Para evitar isso, são incorporadas duas entradas adicionais: uma seed do cliente (definida pelo jogador) e um nonce (um número inteiro que aumenta a cada aposta).

Todos os três são concatenados e submetidos ao algoritmo SHA3-256 para gerar a assinatura da rodada:

roundSignature = SHA3-256(`${serverSeed}:${clientSeed}:${nonce}`)

A seed do cliente garante que o cassino não possa prever o resultado no momento do compromisso, já que a entrada do jogador é desconhecida quando a seed do servidor é gerada. O nonce garante que cada rodada gere uma assinatura distinta, mesmo quando ambas as seeds permanecem inalteradas entre as apostas.

Geração de números aleatórios com o SHAKE256

A assinatura completa é inserida em SHAKE256, uma função de saída extensível (XOF) da família SHA-3. Ao contrário de um hash padrão, um XOF pode gerar um fluxo de bytes pseudoaleatórios de comprimento arbitrário a partir de uma entrada fixa, o que pode ser utilizado neste caso, pois a instalação de várias minas requer vários valores aleatórios independentes.

A implementação extrai 4 bytes por vez do fluxo. Quando a saída inicial se esgota, ele acrescenta " próximo" ao estado e comprime novamente, embora, na prática, uma grade de 25 blocos com qualquer número realista de minas caiba perfeitamente nos primeiros 256 bits.

Posicionamento imparcial de minas: amostragem por rejeição

Utilizar uma simples operação de módulo para realizar a conversão de um inteiro aleatório de 32 bits em uma posição na grade apresenta um problema sutil, mas real: a menos que 2^32 for divisível por 2 entre o número de posições restantes P, algumas posições serão ligeiramente mais prováveis do que outras.

A Cloudbet elimina isso por meio da amostragem por rejeição:

javascript
const maxAcceptable = Math.floor(0x100000000 / P) * P;
let rand;
do { rand = rng.next().value; } while (rand >= maxAcceptable);
const index = rand % P;

Qualquer valor fora do maior múltiplo de P que caiba em 32 bits é descartado, e o próximo valor do fluxo SHAKE256 é sorteado em seu lugar. O que resta é uma distribuição uniforme por exatamente P posições.

Após a colocação de cada mina, essa posição é removida do conjunto e P é reduzido em 1, uma reorganização parcial de Fisher-Yates sobre a matriz plana de 25 elementos.

Verificação de uma rodada

A verificação pós-jogo requer quatro valores, todos divulgados pela Cloudbet após a rodada:

  • serverSeed
  • clientSeed
  • nonce
  • As minePositions declaradas

As etapas de verificação são:

  1. Hash serverSeed com SHA3-256 e confirme se ele corresponde ao compromisso pré-jogo.
  2. Calcule roundSignature = SHA3-256(serverSeed:clientSeed:nonce).
  3. Inicialize o fluxo SHAKE256 com roundSignature e execute o loop de amostragem de rejeição para reproduzir independentemente minePositions.

Uma correspondência confirma que a grade não foi alterada após o início da rodada.

Exemplo de implementação (Node.js)

bash
npm install js-sha3
javascript
const crypto = require('crypto');
const { sha3_256, shake256 } = require('js-sha3');

function generateServerSeed() {
  const seed = crypto.randomBytes(32).toString('hex');
  return { serverSeed: seed, commitment: sha3_256(seed) };
}

function createRoundSignature(serverSeed, clientSeed, nonce) {
  return sha3_256(`${serverSeed}:${clientSeed}:${nonce}`);
}

function* createShake256Stream(signature) {
  const byteStream = shake256.create(32);
  byteStream.update(signature);
  while (true) {
    const buf = Buffer.from(byteStream.digest({ buffer: true }));
    for (let i = 0; i < buf.length; i += 4) {
      if (i + 4 <= buf.length) yield buf.readUInt32BE(i);
    }
    byteStream.update('next');
  }
}

function pickUniquePositions(count, totalTiles, rng) {
  const available = Array.from({ length: totalTiles }, (_, i) => i);
  const result = [];
  for (let i = 0; i < count; i++) {
    const P = available.length;
    const maxAcceptable = Math.floor(0x100000000 / P) * P;
    let rand;
    do { rand = rng.next().value; } while (rand >= maxAcceptable);
    const index = rand % P;
    result.push(available[index]);
    available.splice(index, 1);
  }
  return result;
}

const { serverSeed, commitment } = generateServerSeed();
const clientSeed = 'player-provided-seed';
const nonce = 0;
const sig = createRoundSignature(serverSeed, clientSeed, nonce);
const rng = createShake256Stream(sig);
const mines = pickUniquePositions(5, 25, rng);

console.log('Commitment:', commitment);
console.log('Server seed (post-game):', serverSeed);
console.log('Mine positions:', mines);

Algumas coisas que vale a pena destacar

Por que o SHA3 em vez do SHA2? Para um compromisso simples, qualquer uma das opções serviria. A estrutura em esponja do SHA3 realmente elimina os ataques de extensão de comprimento, e utilizar-lhe em todo o processo, tanto para o compromisso quanto como base para o SHAKE256, mantém a superfície criptográfica consistente.

Por que usar o SHAKE256 em vez de um PRNG com seed? Determinismo e auditabilidade. O SHAKE256 é uma primitiva padronizada com uma especificação bem definida, o que significa que implementações independentes em qualquer linguagem produzirão resultados idênticos a partir da mesma entrada. Um PRNG personalizado introduz um comportamento definido pela implementação que dificulta a verificação por terceiros.

Um jogador pode burlar o sistema escolhendo uma seed favorável no cliente? Em princípio, sim. Um jogador poderia testar diferentes sementes do cliente para encontrar uma que gerasse uma grade favorável. Isso é considerado aceitável no contexto do projeto: o direito do jogador de escolher sua seed é uma funcionalidade, não uma vulnerabilidade. A exposição do cassino é simétrica e, o que é fundamental, o mesmo mecanismo que permite ao jogador escolher uma boa seed também lhe permite provar que o cassino nunca manipulou a sua.

O jogo Mines é um ponto de entrada simples para o design provably fair, justamente porque seu fluxo de geração de números aleatórios (RNG) é relativamente autônomo: um único ciclo de “commit-reveal”, um fluxo de bytes simples e uma técnica de amostragem bem compreendida. À medida que a série avança, veremos como outros Cloudbet Originals adaptam e ampliam esses mesmos fundamentos, às vezes com fontes adicionais de entropia, diferentes mapeamentos de resultados ou verificação em múltiplas etapas, ao mesmo tempo em que preservam a mesma garantia essencial: que nenhum resultado pode ser determinado depois que o jogador tiver confirmado sua ação.

Compartilhe esta publicação


Você também pode gostar

Veja nosso cassino e casa de apostas esportivas em ação
Cloudbet Academy
Fique mais informado
Receba recompensas

Conclua as aulas gratuitas da Academia e ganhe códigos promocionais exclusivos.