Ir al contenido principal

Provably Fair - Implementación

¿Cómo se implementa Provably Fair en el código?

Suponiendo que el juego ha terminado y contamos con la semilla del servidor sin hash, la semilla del cliente y el nonce, así es como funciona.

Se requerirán tres pasos principales para generar los resultados del juego.

  1. byteGenerator (generación de bytes aleatorios)

  2. generateFloats (conversión de bytes a números flotantes [dígitos])

  3. De números flotantes a eventos del juego (conversión de números flotantes en eventos reales en los juegos originales)

Función ByteGenerator como generador de bytes aleatorios Bytes Generador

La byteGenerator función sirve como un generador de bytes aleatorios.

Utiliza valores únicos de clientSeed, serverSeed, nonce, y cursor para generar un valor con hash SHA-256 aleatorio y único mediante la función criptográfica HMAC_SHA256.

El valor SHA-256 generado tiene un tamaño de 32 bytes. Para garantizar un equilibrio entre un resultado del juego suficientemente aleatorio y la intensidad computacional, los 32 bytes se dividen en 8 secciones de 4 bytes* cada una para generar cada resultado del juego.

En ciertos juegos en los que se requiere más de 8 resultado de juego, utilizaremos el cursor. El cursor comienza inicialmente en 0 y aumenta a 1,2,3,4 para cumplir con el requisito de resultados,

En los juegos en los que no requerimos más de 8 resultados aleatorios, el cursor no aumenta de valor.

*4 bytes de datos nos darán 2^32 (4,294,967,296) resultados posibles, lo cual constituye un conjunto suficientemente grande para la aleatoriedad.

function* byteGenerator({ serverSeed, clientSeed, nonce, cursor }: ByteGeneratorInterface) {

// Setup cursor variables let currentRound = Math.floor(cursor / 32);
let currentRoundCursor = cursor;
currentRoundCursor -= currentRound * 32;

// Generate outputs until cursor requirement fullfilled
while (true) {
// HMAC function used to output provided inputs into bytes
const hmac = crypto.createHmac('sha256', serverSeed);
hmac.update(`${clientSeed}:${nonce}:${currentRound}`);
const buffer = hmac.digest();

// Update curser for next iteration of loop
while (currentRoundCursor < 32) {
yield Number(buffer[currentRoundCursor]);
currentRoundCursor += 1;
}
currentRoundCursor = 0;
currentRound += 1;
}
}

GenerateFloats: función para convertir bytes en números flotantes

Esta función convierte el valor hexadecimal SHA-256 de bytes a números flotantes para usarlo en cálculos posteriores de eventos del juego.

A continuación se ilustra cómo un valor hexadecimal SHA-256 se convierte de bytes a números flotantes. El resultado final, numArr, contiene todos los resultados posibles requeridos por el juego. Si solo se requiere 1 resultado, la lista contendrá un solo valor.

Devuelve un arreglo de números entre 0-1, donde count representa la cantidad de elementos en el arreglo devuelto.

Código:

// Convert the hash output from the rng byteGenerator to floats 
export function generateFloats({
serverSeed,
clientSeed,
nonce,
cursor,
count,
}: GenerateFloatsInterface) {
// Random number generator function
const rng = byteGenerator({ serverSeed, clientSeed, nonce, cursor });
// Declare bytes as empty array
const bytes = [];

// Populate bytes array with sets of 4 from RNG output
while (bytes.length < count * 4) {
bytes.push(rng.next().value!);
}

// Return bytes as floats using lodash reduce function
const numArr = chunk(bytes, 4).map(bytesChunk =>
bytesChunk.reduce((result, value, i) => {
const divider = 256 ** (i + 1);
const partialResult = value / divider;
return result + partialResult;
}, 0),
);
return numArr;
}

Todos nuestros juegos originales utilizan ambas funciones ByteGenerator y GenerateFloats para generar números flotantes aleatorios entre 0 y 1. Sin embargo, a partir de este punto, cada juego sigue un procedimiento único para determinar el evento del juego a partir del número flotante generado.

El procedimiento único se explicará en detalle en Eventos del juego.

Ejemplo ilustrado

Aquí ilustraremos cómo se utilizan las entradas para generar un evento de juego de dados

Input Values: Given some random input values 
serverSeed, clientSeed, nonce, cursor


Step 1: byteGenerator creates a SHA-256 byte
"a3f4e0ac7c7e8e9b5f16106c6b1d14e87c2c5a8d59b1d1c6a0b5f3e5a7d4c9a8”


Step 2: 256 bytes is split into 8 equal set of 32 bytes
"a3f4e0ac”, “7c7e8e9b”, “5f16106c”, “6b1d14e8”,
“7c2c5a8d”, “59b1d1c6”, “a0b5f3e5”, “a7d4c9a8”


Step 3: Each set is broken down into 2 bytes each
(only first 2 sets is shown)
set 1: a3-f4-e0-ac
set 2: 7c-7e-8e-9b
......


Step 4: Each of the 2 bytes represent a number from 0 to 255
set 1: [163, 244, 224, 172]
set 2: [124, 126, 142, 155]
......


Step 5: Using the formula in the code to generate numArr
Float 1 = (163 / (256^1)) + (244 / (256^2)) + (224 / (256^3))
+ (172 / (256^4)) = 0.64045528601
Float 2 = (124 / (256^1)) + (126 / (256^2)) + (142 / (256^3))
+ (155 / (256^4)) = 0.48630610737 Float 3 ......


Step 6: Output the floats in a list
numArr = [0.64045528601, 0.48630610737, ...... ]

*** Game Event Generation ***
Step 7: The numArr output will be used to generate game events,
depending on the game requirement


Example 7: Using dice as an example.
The game event is generated using the first numArr value.
const resultValue = floats.map(val => Math.floor(floats * 10001) / 100);

resultValue = Math.floor(0.64045528601 * 10001) / 100
= 64.05

Nota: En realidad, cuando un juego está activo, la semilla del servidor tiene hash, por lo que el jugador y el operador no podrán ver el resultado individual de este proceso durante el juego.

Dado que se utilizan el mismo algoritmo y las mismas funciones durante y después del juego, los usuarios siempre pueden demostrar el resultado con las mismas entradas.

¿Ha quedado contestada tu pregunta?