Cách Provably Fair được triển khai trong mã nguồn?
Giả sử trò chơi đã kết thúc và chúng ta có các giá trị sau: server seed chưa băm, client seed, và nonce, thì quy trình sẽ hoạt động như sau.
Có 3 bước chính để tạo ra kết quả trò chơi:
byteGenerator (Tạo ngẫu nhiên các byte)
generateFloats (Chuyển byte thành số thực)
Float to game events (Chuyển đổi số thực thành sự kiện trong trò chơi gốc)
Hàm ByteGenerator – Trình tạo byte ngẫu nhiên
Hàm byteGenerator
là trình tạo byte ngẫu nhiên.
Nó sử dụng các giá trị duy nhất gồm clientSeed
, serverSeed
, nonce
, và cursor
để tạo ra một giá trị SHA-256 được băm bằng HMAC_SHA256.
Giá trị SHA-256 được tạo ra có kích thước 32 byte. Để đảm bảo kết quả đủ ngẫu nhiên mà không quá nặng về xử lý, 32 byte này được chia thành 8 phần, mỗi phần 4 byte để tạo kết quả trò chơi.
Trong các trò chơi cần hơn 8 kết quả, chúng tôi sử dụng cursor, bắt đầu từ 0 và tăng dần đến 1, 2, 3,... tùy theo số kết quả cần sinh ra.
Với các trò chơi chỉ cần ≤ 8 kết quả ngẫu nhiên, cursor
không thay đổi.
Ghi chú: 4 byte = 2³² = 4,294,967,296 kết quả khả thi – đủ cho mức độ ngẫu nhiên cao.
Code mẫu:
function* byteGenerator({ serverSeed, clientSeed, nonce, cursor }: ByteGeneratorInterface) {
let currentRound = Math.floor(cursor / 32);
let currentRoundCursor = cursor;
currentRoundCursor -= currentRound * 32;
while (true) {
const hmac = crypto.createHmac('sha256', serverSeed);
hmac.update(`${clientSeed}:${nonce}:${currentRound}`);
const buffer = hmac.digest();
while (currentRoundCursor < 32) {
yield Number(buffer[currentRoundCursor]);
currentRoundCursor += 1;
}
currentRoundCursor = 0;
currentRound += 1;
}
}
Hàm GenerateFloats – Chuyển đổi Byte thành Float
Hàm này chuyển giá trị byte SHA-256 thành số thực (float) để sử dụng trong logic xác định sự kiện trong game.
Bên dưới là minh họa cách một giá trị SHA-256 dạng byte được chuyển thành số thực. Đầu ra cuối cùng, numArr
, chứa tất cả các kết quả khả thi được yêu cầu bởi trò chơi. Nếu chỉ cần 1 kết quả, danh sách chỉ có 1 phần tử.
Hàm trả về mảng số thực nằm trong khoảng 0 đến 1. Số lượng phần tử trong mảng tùy vào tham số count
.
Code mẫu:
export function generateFloats({
serverSeed,
clientSeed,
nonce,
cursor,
count,
}: GenerateFloatsInterface) {
const rng = byteGenerator({ serverSeed, clientSeed, nonce, cursor });
const bytes = [];
while (bytes.length < count * 4) {
bytes.push(rng.next().value!);
}
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;
}
Tất cả các trò chơi gốc (original games) của chúng tôi sử dụng hai hàm byteGenerator và generateFloats để tạo ra giá trị ngẫu nhiên từ 0 đến 1. Từ đó, mỗi trò chơi sẽ có thuật toán riêng để biến các giá trị này thành sự kiện thực tế trong game.
Cách chuyển đổi này sẽ được trình bày chi tiết trong phần Game Events.
Ví dụ minh họa
Dưới đây là quy trình minh họa việc sinh ra kết quả trò chơi xí ngầu (dice):
Giá trị đầu vào: serverSeed, clientSeed, nonce, cursor
Bước 1: byteGenerator tạo ra giá trị SHA-256 dạng byte
a3f4e0ac7c7e8e9b5f16106c6b1d14e87c2c5a8d59b1d1c6a0b5f3e5a7d4c9a8
Bước 2: Chia 256-bit thành 8 phần bằng nhau, mỗi phần 4 byte
"a3f4e0ac", "7c7e8e9b", "5f16106c", "6b1d14e8", "7c2c5a8d", "59b1d1c6", "a0b5f3e5", "a7d4c9a8"
Bước 3: Tách từng phần thành mảng 4 bytes
Chỉ hiển thị 2 phần đầu:
set 1: a3-f4-e0-ac
set 2: 7c-7e-8e-9b
Bước 4: Chuyển byte sang số (0–255)
set 1: [163, 244, 224, 172]
set 2: [124, 126, 142, 155]
Bước 5: Áp dụng công thức chuyển sang float
Float 1 = (163 / 256¹) + (244 / 256²) + (224 / 256³) + (172 / 256⁴)= 0.64045528601
Float 2 = (124 / 256¹) + (126 / 256²) + (142 / 256³) + (155 / 256⁴)= 0.48630610737
Bước 6: Mảng kết quả đầu ra
numArr = [0.64045528601, 0.48630610737, ...]
Game Event Generation
Bước 7: Sử dụng numArr để sinh ra kết quả trong game, tùy theo yêu cầu
Ví dụ: Trò chơi xí ngầu sử dụng float đầu tiên:
javascriptCopyEditconst resultValue = Math.floor(0.64045528601 * 10001) / 100; // Kết quả: 64.05
Lưu ý: Trong khi trò chơi đang diễn ra, server seed được băm nên người chơi và hệ thống không thể xem kết quả byte thực tế.
Tuy nhiên, vì cùng một thuật toán và hàm được sử dụng trong quá trình chơi và khi xác minh, người chơi luôn có thể kiểm tra và chứng minh kết quả nếu biết được các input: client seed, server seed, và nonce.