Aby zapewnić przejrzystość i sprawdzalną uczciwość, wygenerowaliśmy łańcuch 10 milionów skrótów SHA256 dla każdej gry wieloosobowej, zaczynając od tajemnego klucza serwera, który wielokrotnie zasilał wyjście SHA256 wstecz na siebie 10 milionów razy
SHA256 ostatecznego skrótu dla Platform to: eeb4d340bfaed702601b9a08faba2f8088c4810c3e3a883293accaf25053b883
SHA256 ostatecznego skrótu dla Crash to: c46f0705f6ba4df891ce44accda8f89f5a6fa8b987eb7c7b445280cf6cabfbc6
SHA256 ostatecznego skrótu dla Battle to: 63cb1eb64ec4eb4de02f6bad85e42365d08f37497a1b7e907bd0b9183f27e1b3
Publikując to tutaj, uniemożliwiamy możliwość wyboru alternatywnego łańcucha SHA256. Teraz serwer gry odtwarzauje ten łańcuch skrótów w odwrotnej kolejności, używając tych wartości do obliczenia wyników gry w sposób sprawdzalnie uczciwy.
1. Tajny Skrót Początkowy: Każdy typ gry wieloosobowej ma swój prywatny skrót początkowy (znany tylko serwerowi), stanowiący pierwiastek unikalnego łańcucha skrótów.
2. Generowanie Łańcucha Skrótów: Od początkowego tajnego skrótu generujemy łańcuch 10 000 000 skrótów poprzez wielokrotne zastosowanie SHA256.
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. Konsumpcja Wstecz: Ujawniamy ostateczny skrót w łańcuchu publicznie. Każda runda gry konsumuje skróty w odwrotnej kolejności: Runda 1 używa hash[10_000_000], Runda 2 używa hash[9_999_999], itd...
4. Ten projekt uniemożliwia fałszowanie przyszłych skrótów, a jednocześnie umożliwia każdemu zweryfikowanie wszystkich poprzednich skrótów.
Nieprzewidywalność: Przyszłe wyniki gry nie mogą być znane ani na nie wpływać nawet serwer.
Weryfikowalność: Gracze mogą zweryfikować integralność dowolnej poprzedniej rundy, używając tylko publicznie ujawnionego ostatecznego skrótu.
Izolacja Gry: Każdy typ gry ma swój łańcuch, zapobiegając manipulacji między grami.
Aby zapewnić sprawdzalną uczciwość w grach jednoosobowych, nasza platforma wykorzystuje system oparty na trzech głównych komponentach do wygenerowania ziarna wyniku:
1. Ziarno Klienta: To ziarno kontrolowane przez użytkownika. Jest automatycznie generowane przy rejestracji użytkownika, ale użytkownik może je zmienić w dowolnym momencie. Zmiana ziarna klienta powoduje wygenerowanie nowego ziarna serwera.
2. Ziarno Serwera: Jest generowane przez serwer przy użyciu wewnętrznych parametrów, które nie są ujawniane użytkownikowi. Jest jednoznacznie powiązane z bieżącym ziarnem klienta. Dla każdego ziarna klienta istnieje odpowiednie ziarno serwera. Gdy użytkownik zmienia ziarno klienta, poprzednie ziarno serwera jest ujawniane w celach weryfikacji.
3. Nonce: Unikalna liczba oparta na znaczniku czasowym zakładu (w milisekundach). Zapewnia, że nawet powtórzone działania dają różne wyniki.
Te trzy wartości są łączone i haszowane za pomocą HMAC-SHA256 w celu wygenerowania ostatecznego ziarna gry, które jest następnie używane do określenia wyniku gry.
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
}
Serwer zapewnia haszowaną wersję bieżącego ziarna serwera przed dokonaniem jakiegokolwiek zakładu.
Po zmianie ziarna klienta przez użytkownika poprzednio używane ziarno serwera jest ujawniane.
Pozwala to użytkownikom zweryfikować, że wszystkie wyniki wygenerowane z poprzednim ziarnem serwera były spójne i uczciwe.