Afin de garantir la transparence et l'équité vérifiable, nous avons généré une chaîne de 10 millions de hachages SHA256 pour chaque jeu multijoueur, en commençant par un secret serveur dont la sortie SHA256 a été réinjectée en boucle sur elle-même 10 millions de fois.
Le SHA256 du hachage final pour Platform est : eeb4d340bfaed702601b9a08faba2f8088c4810c3e3a883293accaf25053b883
Le SHA256 du hachage final pour Crash est : c46f0705f6ba4df891ce44accda8f89f5a6fa8b987eb7c7b445280cf6cabfbc6
Le SHA256 du hachage final pour Battle est : 63cb1eb64ec4eb4de02f6bad85e42365d08f37497a1b7e907bd0b9183f27e1b3
En le publiant ici, nous empêchons toute possibilité de choisir une chaîne SHA256 alternative. Le serveur de jeu parcourt désormais cette chaîne de hachages dans l'ordre inverse, en utilisant ces valeurs pour calculer les résultats de jeu de manière vérifiablement équitable.
1. Hachage Initial Secret : Chaque type de jeu multijoueur possède son propre hachage initial privé (connu uniquement du serveur), formant la racine d'une chaîne de hachage unique.
2. Génération de la Chaîne de Hachage : À partir du hachage initial secret, nous générons une chaîne de 10 000 000 de hachages en appliquant SHA256 de manière répétée.
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. Consommation Inversée : Nous révélons publiquement le hachage final de la chaîne. Chaque round de jeu consomme les hachages dans l'ordre inverse : Le Round 1 utilise hash[10_000_000] Le Round 2 utilise hash[9_999_999] et ainsi de suite…
4. Cette conception rend impossible de falsifier de futurs hachages, tout en permettant à quiconque de vérifier tous les hachages précédents.
Imprévisibilité : Les résultats futurs des jeux ne peuvent être connus ni influencés, même par le serveur.
Vérifiabilité : Les joueurs peuvent vérifier l'intégrité de n'importe quel round précédent en utilisant uniquement le hachage final rendu public.
Isolation des jeux : Chaque type de jeu possède sa propre chaîne, empêchant toute manipulation croisée entre jeux.
Afin de garantir une équité vérifiable dans les jeux solo, notre plateforme utilise un système basé sur trois composants principaux pour générer la graine de résultat :
1. Graine Client : Il s'agit d'une graine contrôlée par l'utilisateur. Elle est générée automatiquement lors de l'inscription de l'utilisateur, mais celui-ci peut la modifier à tout moment. La modification de la graine client déclenche la génération d'une nouvelle graine serveur.
2. Graine Serveur : Elle est générée par le serveur à l'aide de paramètres internes non divulgués à l'utilisateur. Elle est liée de manière unique à la graine client actuelle. Pour chaque graine client, il existe une graine serveur correspondante. Lorsque l'utilisateur modifie la graine client, la graine serveur précédente est révélée à des fins de vérification.
3. Nonce : Un numéro unique basé sur l'horodatage de la mise (en millisecondes). Il garantit que même des actions répétées produisent des résultats différents.
Ces trois valeurs sont combinées et hachées à l'aide de HMAC-SHA256 pour générer la graine de jeu finale, qui est ensuite utilisée pour déterminer le résultat du jeu.
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
}
Le serveur fournit la version hachée de la graine serveur actuelle avant que toute mise ne soit effectuée.
Après que l'utilisateur a modifié sa graine client, la graine serveur précédemment utilisée est révélée.
Cela permet aux utilisateurs de vérifier que tous les résultats générés avec la graine serveur précédente étaient cohérents et équitables.