To ensure transparency and provable fairness we have generated a chain of 10 million SHA256 hashes for each multiplayer game, starting with a server secret that has been repeatedly fed the output of SHA256 back into itself 10 million times
The SHA256 of the final hash for Platform is: eeb4d340bfaed702601b9a08faba2f8088c4810c3e3a883293accaf25053b883
The SHA256 of the final hash for Crash is: c46f0705f6ba4df891ce44accda8f89f5a6fa8b987eb7c7b445280cf6cabfbc6
The SHA256 of the final hash for Battle is: 63cb1eb64ec4eb4de02f6bad85e42365d08f37497a1b7e907bd0b9183f27e1b3
By publicising it here we are preventing any ability to pick an alternate SHA256 chain. Now the game server is playing through this chain of hashes in reverse order, using these values to calculate the game results provably fair.
1. Secret Initial Hash: Each multiplayer game type has its own private initial hash (known only to the server), forming the root of a unique hash chain.
2. Hash Chain Generation: From the initial secret hash, we generate a chain of 10,000,000 hashes by applying SHA256 repeatedly.
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. Reverse Consumption: We reveal the final hash in the chain to the public. Each game round consumes the hashes in reverse order: Round 1 uses hash[10_000_000] Round 2 uses hash[9_999_999] and so onβ¦
4. This design makes it impossible to forge future hashes, while allowing anyone to verify all previous hashes.
Unpredictability: Future game outcomes cannot be known or influenced, even by the server.
Verifiability: Players can verify the integrity of any previous round using only the publicly revealed final hash.
Game Isolation: Each game type has its own chain, preventing cross-game manipulation.
To ensure provable fairness in single-player games, our platform uses a system based on three main components to generate the result seed:
1. Client Seed: This is a user-controlled seed. It is automatically generated when a user registers, but the user can change it at any time. Changing the client seed initiates the generation of a new server seed.
2. Server Seed: This is generated by the server using internal parameters that are not disclosed to the user. It is uniquely bound to the current client seed. For each client seed, there is a corresponding server seed. When the user changes the client seed, the previous server seed is revealed for verification purposes.
3. Nonce: A unique number that is based on the timestamp of the bet (in milliseconds). It ensures that even repeated actions produce different outcomes.
These three values are combined and hashed using HMAC-SHA256 to generate the final game seed, which is then used to determine the result of the game.
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
}
The server provides the hashed version of the current server seed before any bet is made.
After the user changes their client seed, the previously used server seed is revealed.
This allows users to verify that all results generated with the previous server seed were consistent and fair.