Ugrás a fő tartalomra

Provably Fair – Megvalósítás

Hogyan valósul meg a Provably Fair a kódban?

Feltételezve, hogy a játék befejeződött, és rendelkezésünkre áll a nem hashelt szerver seed, kliens seed és nonce, a folyamat a következőképpen működik.

A játékeredmények létrehozásához három fő lépés szükséges.

  1. byteGenerator (véletlenszerű bájtok létrehozása)

  2. generateFloats (bájtok lebegőpontos számokká alakítása)

  3. Lebegőpontos számokból játékesemények (a lebegőpontos számok tényleges eseményekké alakítása az eredeti játékokban)

A ByteGenerator függvény véletlenszerű bájt generátorként

A byteGenerator függvény véletlenszerű bájtgenerátorként szolgál.

A következő egyedi értékeket használja: clientSeed, serverSeed, nonce, valamint cursor értékeket, hogy a kriptográfiai HMAC_SHA256 függvény használatával véletlenszerű és egyedi SHA-256 hashelt értéket hozzon létre.

A létrehozott SHA-256 érték mérete 32 bájt. A kellően véletlenszerű játékkimenetel és a számítási igény közötti egyensúly érdekében a 32 bájtot 8 darab, egyenként 4 bájtos szakaszra* osztjuk az egyes játékeredmények létrehozásához.

Bizonyos játékokban, ahol egynél több 8 játékeredményre van szükség, a következőt használjuk: cursor. A kurzor kezdetben 0 értéken indul, majd 1,2,3,4 értékre növekszik az eredménykövetelmény teljesítéséhez,

Azoknál a játékoknál, amelyekhez nincs szükség egynél több 8 véletlenszerű kimenetelre, a kurzor értéke nem növekszik.

*4 bájt adat 2^32 (4,294,967,296) lehetséges kimenetelt biztosít, ami kellően nagy halmaz a véletlenszerűséghez.

function* byteGenerator({ serverSeed, clientSeed, nonce, cursor }: ByteGeneratorInterface) {

// Setup cursor variables let currentRound = Math.floor(cursor / 32);
let currentRoundCursor = cursor;
currentRoundCursor -= currentRound * 32;

// Generate outputs until cursor requirement fullfilled
while (true) {
// HMAC function used to output provided inputs into bytes
const hmac = crypto.createHmac('sha256', serverSeed);
hmac.update(`${clientSeed}:${nonce}:${currentRound}`);
const buffer = hmac.digest();

// Update curser for next iteration of loop
while (currentRoundCursor < 32) {
yield Number(buffer[currentRoundCursor]);
currentRoundCursor += 1;
}
currentRoundCursor = 0;
currentRound += 1;
}
}

GenerateFloats függvény a bájtok lebegőpontos számokká alakításához

Ez a függvény a SHA-256 hexadecimális értékét bájtokból lebegőpontos számokká alakítja, hogy további számításokhoz felhasználható legyen a játékesemények során.

Az alábbiak bemutatják, hogyan alakítható át egy SHA-256 hexadecimális érték bájtból lebegőpontos számmá. A végső kimenet, a numArr, tartalmazza a játék által igényelt összes lehetséges kimenetelt. Ha csak 1 kimenetel szükséges, a lista csak egy értéket tartalmaz.

A 0-1 közötti számok tömbjét adja vissza, ahol a count a visszaadott tömb elemeinek számát jelöli.

Kód:

// Convert the hash output from the rng byteGenerator to floats 
export function generateFloats({
serverSeed,
clientSeed,
nonce,
cursor,
count,
}: GenerateFloatsInterface) {
// Random number generator function
const rng = byteGenerator({ serverSeed, clientSeed, nonce, cursor });
// Declare bytes as empty array
const bytes = [];

// Populate bytes array with sets of 4 from RNG output
while (bytes.length < count * 4) {
bytes.push(rng.next().value!);
}

// Return bytes as floats using lodash reduce function
const numArr = chunk(bytes, 4).map(bytesChunk =>
bytesChunk.reduce((result, value, i) => {
const divider = 256 ** (i + 1);
const partialResult = value / divider;
return result + partialResult;
}, 0),
);
return numArr;
}

Minden Shuffle Original játékunk használja mind a ByteGenerator, mind a GenerateFloats függvényt a 0 és 1 közötti véletlenszerű lebegőpontos számok létrehozásához. Ettől a ponttól kezdve azonban minden játék egyedi eljárást alkalmaz a játékesemény meghatározására a létrehozott lebegőpontos számból.

Az egyedi eljárást részletesen a következő rész ismerteti: Játékesemények.

Szemléltető példa

Itt bemutatjuk, hogyan használhatók a bemenetek egy kockajáték játékeseményének létrehozására.

Input Values: Given some random input values 
serverSeed, clientSeed, nonce, cursor


Step 1: byteGenerator creates a SHA-256 byte
"a3f4e0ac7c7e8e9b5f16106c6b1d14e87c2c5a8d59b1d1c6a0b5f3e5a7d4c9a8”


Step 2: 256 bytes is split into 8 equal set of 32 bytes
"a3f4e0ac”, “7c7e8e9b”, “5f16106c”, “6b1d14e8”,
“7c2c5a8d”, “59b1d1c6”, “a0b5f3e5”, “a7d4c9a8”


Step 3: Each set is broken down into 2 bytes each
(only first 2 sets is shown)
set 1: a3-f4-e0-ac
set 2: 7c-7e-8e-9b
......


Step 4: Each of the 2 bytes represent a number from 0 to 255
set 1: [163, 244, 224, 172]
set 2: [124, 126, 142, 155]
......


Step 5: Using the formula in the code to generate numArr
Float 1 = (163 / (256^1)) + (244 / (256^2)) + (224 / (256^3))
+ (172 / (256^4)) = 0.64045528601
Float 2 = (124 / (256^1)) + (126 / (256^2)) + (142 / (256^3))
+ (155 / (256^4)) = 0.48630610737 Float 3 ......


Step 6: Output the floats in a list
numArr = [0.64045528601, 0.48630610737, ...... ]

*** Game Event Generation ***
Step 7: The numArr output will be used to generate game events,
depending on the game requirement


Example 7: Using dice as an example.
The game event is generated using the first numArr value.
const resultValue = floats.map(val => Math.floor(floats * 10001) / 100);

resultValue = Math.floor(0.64045528601 * 10001) / 100
= 64.05

Megjegyzés: A valóságban, amikor egy játék aktív, a szerver seed hashelve van, ezért a játékos és az üzemeltető sem tudja megtekinteni ennek a folyamatnak az egyedi kimenetelét a játék során.

Mivel ugyanazt az algoritmust és függvényeket használjuk a játék során és azt követően is, a felhasználók azonos bemenetek esetén mindig bizonyítani tudják az eredményt.

Választ kapott a kérdésére?