Para garantizar transparencia y equidad comprobable, hemos generado una cadena de 10 millones de hashes SHA256 para cada juego multijugador, comenzando con un secreto del servidor que ha sido alimentado repetidamente con la salida de SHA256 nuevamente en sí mismo 10 millones de veces
El SHA256 del hash final para Platform es: eeb4d340bfaed702601b9a08faba2f8088c4810c3e3a883293accaf25053b883
El SHA256 del hash final para Crash es: c46f0705f6ba4df891ce44accda8f89f5a6fa8b987eb7c7b445280cf6cabfbc6
El SHA256 del hash final para Battle es: 63cb1eb64ec4eb4de02f6bad85e42365d08f37497a1b7e907bd0b9183f27e1b3
Al publicarlo aquí, estamos evitando la posibilidad de elegir una cadena SHA256 alternativa. Ahora el servidor de juegos está reproduciéndose a través de esta cadena de hashes en orden inverso, utilizando estos valores para calcular los resultados de manera demostrativamente justa.
1. Hash Inicial Secreto: Cada tipo de juego multijugador tiene su propio hash inicial privado (conocido solo por el servidor), formando la raíz de una cadena hash única.
2. Generación de Cadena Hash: A partir del hash secreto inicial, generamos una cadena de 10.000.000 de hashes aplicando SHA256 repetidamente.
func HashSeed(seed string) string {
hash := sha256.Sum256([]byte(seed))
return hex.EncodeToString(hash[:])
}
func CalculateHashChain(firstHash string) []string {
hashes := make([]string, 10_000_000)
for i := int64(0); i <= 10_000_000; i++ {
firstHash = HashSeed(firstHash)
hashes[i] = firstHash
}
return hashes
}
3. Consumo Inverso: Revelamos el hash final en la cadena al público. Cada ronda de juego consume los hashes en orden inverso: La Ronda 1 usa hash[10_000_000], La Ronda 2 usa hash[9_999_999], y así sucesivamente...
4. Este diseño hace imposible falsificar hashes futuros, mientras permite a cualquiera verificar todos los hashes anteriores.
Impredecibilidad: Los resultados futuros del juego no pueden ser conocidos o influenciados, ni siquiera por el servidor.
Verificabilidad: Los jugadores pueden verificar la integridad de cualquier ronda anterior utilizando solo el hash final revelado públicamente.
Aislamiento de Juegos: Cada tipo de juego tiene su propia cadena, evitando manipulación entre juegos.
Para garantizar equidad comprobable en juegos de un solo jugador, nuestra plataforma utiliza un sistema basado en tres componentes principales para generar la semilla del resultado:
1. Semilla del Cliente: Esta es una semilla controlada por el usuario. Se genera automáticamente cuando un usuario se registra, pero el usuario puede cambiarla en cualquier momento. Cambiar la semilla del cliente inicia la generación de una nueva semilla del servidor.
2. Semilla del Servidor: Es generada por el servidor utilizando parámetros internos que no se divulgan al usuario. Está vinculada únicamente a la semilla del cliente actual. Para cada semilla del cliente, hay una semilla del servidor correspondiente. Cuando el usuario cambia la semilla del cliente, la semilla del servidor anterior se revela con fines de verificación.
3. Nonce: Un número único basado en la marca de tiempo de la apuesta (en milisegundos). Garantiza que incluso las acciones repetidas produzcan resultados diferentes.
Estos tres valores se combinan y se codifican usando HMAC-SHA256 para generar la semilla de juego final, que luego se usa para determinar el resultado del juego.
func GenerateUserGameSeed(userSeed string, userServerSeed string, nonce int64) (string, error) {
data := fmt.Sprintf("%s:%s:%d", userServerSeed, userSeed, nonce)
mac := hmac.New(sha256.New, []byte(data))
gameSeed := hex.EncodeToString(mac.Sum(nil))
return gameSeed, nil
}
El servidor proporciona la versión codificada de la semilla del servidor actual antes de realizar cualquier apuesta.
Después de que el usuario cambia su semilla de cliente, se revela la semilla del servidor utilizada anteriormente.
Esto permite a los usuarios verificar que todos los resultados generados con la semilla del servidor anterior fueron consistentes y justos.