Bagaimana Provably Fair Diimplementasikan dalam kode?
Dengan asumsi bahwa permainan telah selesai dan kita memiliki server seed yang belum di-hash, client seed, serta nonce, berikut cara kerjanya.
Tiga langkah utama diperlukan untuk menghasilkan hasil permainan.
byteGenerator (Pembuatan Byte Acak)
generateFloats (Mengonversi Byte menjadi Float (digit))
Float ke peristiwa permainan (Mengonversi float menjadi peristiwa aktual dalam permainan original)
Fungsi ByteGenerator sebagai Generator Byte Acak
Fungsi byteGenerator berfungsi sebagai generator byte acak.
Fungsi ini menggunakan nilai unik dari clientSeed, serverSeed, nonce, dan cursor untuk menghasilkan nilai hash SHA-256 yang acak dan unik menggunakan fungsi kriptografis HMAC_SHA256 .
Nilai SHA-256 yang dihasilkan berukuran 32 byte. Untuk memastikan keseimbangan antara hasil permainan yang cukup acak dan intensitas komputasi, 32 byte tersebut dibagi menjadi 8 bagian yang masing-masing terdiri dari 4 byte* untuk menghasilkan setiap hasil permainan.
Dalam permainan tertentu yang memerlukan lebih dari 8 hasil permainan, kami akan menggunakan cursor. Kursor awalnya dimulai dari 0, lalu meningkat hingga 1,2,3,4 untuk memenuhi kebutuhan hasil,
Untuk permainan yang tidak memerlukan lebih dari 8 hasil acak, nilai kursor tidak bertambah.
*4 byte data akan memberikan 2^32 (4,294,967,296) kemungkinan hasil, yang merupakan kumpulan yang cukup besar untuk menghasilkan keacakan.
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;
}
}Menghasilkan Float: Fungsi untuk Mengonversi Byte menjadi Float
Fungsi ini mengonversi nilai heksadesimal SHA-256 dari byte menjadi float untuk digunakan dalam perhitungan lebih lanjut bagi Peristiwa Permainan.
Di bawah ini ditunjukkan cara nilai heksadesimal SHA-256 dikonversi dari byte menjadi float. Output akhir, numArr, berisi semua kemungkinan hasil yang diperlukan oleh permainan. Jika hanya 1 hasil yang diperlukan, daftar tersebut hanya akan berisi satu nilai.
Fungsi ini mengembalikan array angka antara 0-1. count menunjukkan jumlah elemen dalam array yang dikembalikan.
Kode:
// 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;
}
Semua permainan original kami menggunakan fungsi ByteGenerator dan GenerateFloats untuk menghasilkan float acak antara 0 hingga 1. Namun, mulai dari sini setiap permainan memiliki prosedur unik untuk menentukan peristiwa permainan dari float yang dihasilkan.
Prosedur unik tersebut akan dijelaskan secara mendetail di Peristiwa Permainan.
Contoh Ilustrasi
Di sini, kami akan mengilustrasikan bagaimana input digunakan untuk menghasilkan peristiwa permainan dadu
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
Catatan: Dalam praktiknya, saat permainan aktif, server seed di-hash sehingga pemain dan operator tidak dapat melihat output individual dari proses ini selama permainan berlangsung.
Karena algoritme dan fungsi yang sama digunakan selama dan setelah permainan, pengguna selalu dapat membuktikan hasilnya dengan input yang sama.
