El juego con tecnología Provably Fair resuelve un problema inherente a los casinos en línea desde sus inicios: ¿cómo puede un jugador confiar en que el resultado de un juego no ha sido manipulado a posteriori? Resulta que la respuesta es la misma herramienta a la que recurren los criptógrafos cada vez que dos partes necesitan ponerse de acuerdo sobre algo y no existe confianza mutua: un esquema de
compromiso y revelación.
A continuación encontrarás una guía detallada del proceso que utiliza Cloudbet para su juego Mines, desde la generación de semillas hasta la colocación de las minas, y se explica cómo cualquier jugador con mínimos conocimientos de programación puede verificar de manera independiente todos los resultados de las partidas que haya jugado.
Este artículo es el primero de una serie que está planeada para incluir análisis técnicos sobre los mecanismos Provably Fair de imparcialidad comprobable que hay detrás de los juegos Cloudbet Originals. Para verificar personalmente cualquier ronda (incluidas las de Mines), echa un vistazo a la calculadora Provably Fair de Cloudbet.
Contents
- 1 Compromiso del resultado previo al inicio de una partida
- 2 Incorporación de la entropía de los jugadores
- 3 La generación de aleatoriedad con SHAKE256
- 4 Colocación de minas sin sesgo: muestreo por rechazo
- 5 Comprobación de una ronda
- 6 Ejemplo de implementación (Node.js)
- 7 Algunos aspectos que vale la pena destacar
Compromiso del resultado previo al inicio de una partida
Antes de cada ronda, el servidor genera una semilla de servidor aleatoria de 32 bytes e inmediatamente publica su hash SHA3-256, lo que se entiende como el compromiso. Este hash es una huella digital unidireccional: demuestra que la semilla existía en ese momento sin revelar cuál es.
Una vez finalizada la partida, Cloudbet revela la semilla original del servidor. Cualquier persona puede analizarlo mediante hash y confirmar que coincide con el compromiso previo al juego, lo que demuestra que la semilla —y, por lo tanto, la disposición de la mina— se fijó antes de que se revelara la primera casilla. No existe posibilidad de que el casino pueda cambiarlo por un valor más favorable.
Incorporación de la entropía de los jugadores
Si solo se utilizara la semilla del servidor, los jugadores dependerían por completo de la aleatoriedad del casino. Para evitarlo, se incorporan dos datos adicionales: una semilla de cliente (que establece el jugador) y un nonce (un número entero que aumenta con cada apuesta).
Los tres se concatenan y someten a un hash con SHA3-256 para generar la firma de ronda:
roundSignature = SHA3-256(`${serverSeed}:${clientSeed}:${nonce}`)
La semilla de cliente garantiza que el casino no pueda predecir el resultado en el momento del compromiso, ya que la información del jugador no se conoce al momento de generar la semilla del servidor. El nonce garantiza que cada ronda genera una firma distinta, incluso cuando ambas semillas permanezcan inalteradas entre apuestas.
La generación de aleatoriedad con SHAKE256
La firma por ronda se introduce con SHAKE256, una función de salida extensible (XOF) de la familia SHA-3. A diferencia de un hash estándar, un XOF puede generar un flujo de bytes pseudoaleatorio de longitud arbitraria a partir de una entrada fija, lo cual resulta útil en este caso, ya que la colocación de varias minas requiere múltiples valores aleatorios independientes.
La implementación extrae 4 bytes a la vez del flujo de datos. Cuando se agota la salida inicial, añade "next! al estado y vuelve a comprimir, aunque en la práctica una cuadrícula de 25 casillas con cualquier número realista de minas cabe perfectamente dentro de los primeros 256 bits.
Colocación de minas sin sesgo: muestreo por rechazo
Convertir un entero aleatorio de 32 bits en una posición de cuadrícula usando una simple operación de módulo introduce un problema sutil pero real: a menos que 2^32 sea divisible equitativamente entre el número de posiciones restantes P, algunas posiciones serán marginalmente más probables que otras.
Cloudbet elimina este problema mediante el muestreo por rechazo:
const maxAcceptable = Math.floor(0x100000000 / P) * P;
let rand;
do { rand = rng.next().value; } while (rand >= maxAcceptable);
const index = rand % P;
Cualquier valor fuera del múltiplo más grande de P que quepa en 32 bits se descarta, y en su lugar se extrae el siguiente valor del flujo SHAKE256. Lo que queda es una distribución uniforme en exactamente P posiciones.
Después de la colocación de cada mina, esa posición se elimina del grupo y P disminuye en 1, una reorganización parcial de Fisher-Yates sobre la matriz de cuadrícula plana de 25 elementos.
Comprobación de una ronda
La comprobación posterior a una partida requiere cuatro valores, todos ellos publicados por Cloudbet después de la ronda:
serverSeed (semilla del servidor)clientSeed (semilla de cliente)nonce- Las
minePositions(posiciones de las minas) declaradas
Los pasos para hacer la comprobación son los siguientes:
- Hacer hash de la
serverSeedcon SHA3-256 y confirmar que coincide con el compromiso previo al juego. - Derivar
roundSignature = SHA3-256(serverSeed:clientSeed:nonce). - Inicializar la secuencia SHAKE256 con
roundSignaturey ejecutar el bucle de muestreo por rechazo para reproducir de forma independienteminePositions.
Si coincide, confirma que la cuadrícula no fue alterada después de iniciada la ronda.
Ejemplo de implementación (Node.js)
npm install js-sha3
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);
Algunos aspectos que vale la pena destacar
¿Por qué SHA3 en lugar de SHA2? Para tener compromiso sencillo, cualquiera de las dos opciones serviría. La estructura de «esponja» de SHA3 elimina los ataques de extensión de longitud, y su uso a lo largo del proceso (tanto para el compromiso como para servir de base para SHAKE256) mantiene la superficie criptográfica coherente.
¿Por qué SHAKE256 en lugar de un PRNG con semilla? Por determinismo y para poder hacer revisiones. SHAKE256 es una primitiva estandarizada con una especificación bien definida, lo que significa que las implementaciones independientes en cualquier lenguaje producirán resultados idénticos a partir de los mismos datos. Un generador de números pseudoaleatorios (PRNG, por sus siglas en inglés) personalizado introduce un comportamiento definido por la implementación que dificulta la verificación por parte de terceros.
¿Puede un jugador burlar el sistema eligiendo una semilla de cliente que le resulte favorable? En principio, sí: un jugador podría probar diferentes semillas de cliente para encontrar una que genere una cuadrícula favorable. Esto se considera aceptable dentro del diseño: el derecho del jugador a elegir su semilla es una característica, no una vulnerabilidad. La exposición del casino es simétrica y, de manera más esencial, el mismo mecanismo que permite a un jugador elegir una buena semilla también le demuestra que el casino nunca ha manipulado la suya.
Mines constituye un buen punto de partida para adentrarse en el diseño de los juegos Provably Fair precisamente porque el proceso de su generador de números aleatorios (RNG, por sus siglas en inglés) es relativamente autónomo, con un único ciclo de compromiso y revelación, un flujo de bytes sencillo y una técnica de muestreo bien conocida. A medida que esta serie avance, veremos cómo otros juegos Cloudbet Originals adaptan y amplían estos mismos fundamentos —a veces con fuentes de entropía adicionales, diferentes asignaciones de resultados o verificaciones en varias etapas— sin dejar de mantener la misma garantía fundamental: que ningún resultado puede decidirse una vez que el jugador se ha comprometido a realizar su acción.


