Läpinäkyvyyden ja todennettavan reiluuden varmistamiseksi olemme luoneet jokaista moninpeliä varten 10 miljoonan SHA256-tiivisteen ketjun. Luominen alkaa palvelimen salaisuudesta, ja SHA256:n tulos syötetään takaisin algoritmiin toistuvasti 10 miljoonaa kertaa.
Platform-pelin viimeisen tiivisteen SHA256-arvo on: eeb4d340bfaed702601b9a08faba2f8088c4810c3e3a883293accaf25053b883
Crash-pelin viimeisen tiivisteen SHA256-arvo on: c46f0705f6ba4df891ce44accda8f89f5a6fa8b987eb7c7b445280cf6cabfbc6
Battle-pelin viimeisen tiivisteen SHA256-arvo on: 63cb1eb64ec4eb4de02f6bad85e42365d08f37497a1b7e907bd0b9183f27e1b3
Julkaisemalla sen täällä estämme vaihtoehtoisen SHA256-ketjun valitsemisen. Pelipalvelin käy nyt tätä tiivisteketjua läpi käänteisessä järjestyksessä ja käyttää arvoja pelitulosten laskemiseen todistettavasti reilulla tavalla.
1. Salainen alkuperäinen tiiviste: Jokaisella moninpelityypillä on oma yksityinen alkuperäinen tiivisteensä, jonka vain palvelin tuntee ja joka muodostaa yksilöllisen tiivisteketjun juuren.
2. Tiivisteketjun luominen: Luomme salaisesta alkuperäisestä tiivisteestä 10 000 000 tiivisteen ketjun soveltamalla SHA256-algoritmia toistuvasti.
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. Käyttäminen käänteisessä järjestyksessä:Paljastamme ketjun viimeisen tiivisteen julkisesti. Jokaisella pelikierroksella tiivisteitä käytetään käänteisessä järjestyksessä: Kierros 1 käyttää arvoa hash[10_000_000] Kierros 2 käyttää arvoa hash[9_999_999] ja niin edelleen…”
4. Tämän rakenteen ansiosta tulevia tiivisteitä on mahdotonta väärentää, mutta kuka tahansa voi varmentaa kaikki aikaisemmat tiivisteet.
Ennakoimattomuus: Tulevia pelituloksia ei voi tietää eikä niihin voi vaikuttaa edes palvelin.
Todennettavuus: Pelaajat voivat varmentaa minkä tahansa aikaisemman kierroksen eheyden käyttämällä ainoastaan julkisesti paljastettua viimeistä tiivistettä.
Pelien eristäminen: Jokaisella pelityypillä on oma ketjunsa, mikä estää pelien välisen manipuloinnin.
Yksinpelien todennettavan reiluuden varmistamiseksi alustamme käyttää tulossiemenen luomiseen järjestelmää, joka perustuu kolmeen pääkomponenttiin:
1. Asiakassiemen: Tämä on käyttäjän hallitsema siemen. Se luodaan automaattisesti käyttäjän rekisteröityessä, mutta käyttäjä voi vaihtaa sitä milloin tahansa. Asiakassiemenen vaihtaminen käynnistää uuden palvelinsiemenen luomisen.
2. Palvelinsiemen: Palvelin luo tämän käyttämällä sisäisiä parametreja, joita ei paljasteta käyttäjälle. Se sidotaan yksilöllisesti nykyiseen asiakassiemeneen. Jokaista asiakassiementä vastaa oma palvelinsiemen. Kun käyttäjä vaihtaa asiakassiemenen, edellinen palvelinsiemen paljastetaan varmentamista varten.
3. Nonce-arvo: Yksilöllinen luku, joka perustuu panoksen aikaleimaan millisekunteina. Se varmistaa, että myös toistuvat toiminnot tuottavat erilaiset lopputulokset.
Nämä kolme arvoa yhdistetään ja tiivistetään HMAC-SHA256-algoritmilla lopullisen pelisiemenen luomiseksi. Pelin lopputulos määritetään tämän siemenen avulla.
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
}
Palvelin toimittaa nykyisen palvelinsiemenen tiivistetyn version ennen panoksen asettamista.
Kun käyttäjä vaihtaa asiakassiemenensä, aikaisemmin käytetty palvelinsiemen paljastetaan.
Näin käyttäjät voivat varmistaa, että kaikki edellisellä palvelinsiemenellä luodut tulokset olivat yhdenmukaisia ja reiluja.