Also quoting to show the post hasn't been edited, and confirming bitcoin block 831500 has not yet been mined.
Welcome to
bustabit's third provably fair seeding event.
Our provably fair system, based on the
first seeding event, is evolving into an advanced multi-party provably fair scheme. We have integrated
ActuallyFair.com's Vx, a third-party service contributing to the algorithm that converts game hashes to game results. This not only helps us have a more secure system, but also preserves all provably fair guarantees while allowing Actually Fair to verify all games on behalf of our players.
1. Starting with a secret, we have generated a chain of 100,000,000 sha256 hashes by recursively hashing the binary value of the previous hash. Every hash in the chain will be used to generate a game result. The first element in the chain is the hash of game #100,000,000 and the last one is the hash of game #1, aka our terminating hash, which is: 567a98370fb7545137ddb53687723cf0b8a1f5e93b1f76f4a1da29416930fa59.
You can verify if a hash is part of the chain with a function like this:
import { sha256 } from "@noble/hashes/sha256";
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
const TERMINATING_HASH = "567a98370fb7545137ddb53687723cf0b8a1f5e93b1f76f4a1da29416930fa59";
function verifyInChain(hash: Uint8Array) {
for (let gameId = 1; gameId < 100e6; gameId++) {
hash = sha256(hash);
if (bytesToHex(hash) === TERMINATING_HASH) {
console.log("hash is in the chain. It is game: ", gameId);
return gameId;
}
}
console.error("hash is not in the chain");
}
Which could be used like:
verifyInChain(
hexToBytes("70eed5c29bde5132f4e41ec8b117a31533e5b055c6c21174d932b377a1855a04")
);
2. Vx will use its BLS public key: b40c94495f6e6e73619aeb54ec2fc84c5333f7a88ace82923946fc5b6c8635b08f9130888dd96e1
749a1d5aab00020e4.
3. We will play through that chain of hashes, in reverse order, and use the hashes to determine the crash point in a provably fair manner.
4. To prove that Actually Fair and bustabit have picked a fair hash chain (and public key) we will also mix in the lowercase, hexadecimal representation of the hash of a Bitcoin block that has not been mined yet:
Bitcoin block 831500. This will be known as our game salt.
5. Vx gives us a signature (
vxSignature) by signing the concatenation of the previous game hash along with the game salt. Vx uses BLS signatures so that it is not possible to generate multiple valid signatures for a particular message. We can validate the signature with something like:
import { bls12_381 as bls } from "@noble/curves/bls12-381";
import { concatBytes, utf8ToBytes } from "@noble/hashes/utils";
const VX_PUBKEY = "b40c94495f6e6e73619aeb54ec2fc84c5333f7a88ace82923946fc5b6c8635b08f9130888dd96e1749a1d5aab00020e4";
function validateSignature(
gameSalt: string, // the hash of block 831500
prevGameHash: Uint8Array,
vxSignature: Uint8Array
) {
const message = concatBytes(prevGameHash, utf8ToBytes(gameSalt));
return bls.verify(vxSignature, message, VX_PUBKEY);
}
5. Once we have a valid
vxSignature, we take the next unused hash from the chain and use it to determine game results:
import { hmac } from "@noble/hashes/hmac";
import { sha256 } from "@noble/hashes/sha256";
import { bytesToHex } from "@noble/hashes/utils";
export function gameResult(vxSignature: Uint8Array, gameHash: Uint8Array) {
const nBits = 52; // number of most significant bits to use
// 1. HMAC_SHA256(key=signature, message=hash)
const hash = bytesToHex(hmac(sha256, vxSignature, gameHash));
// 2. r = 52 most significant bits
const seed = hash.slice(0, nBits / 4);
const r = Number.parseInt(seed, 16);
// 3. X = r / 2^52
let X = r / Math.pow(2, nBits); // uniformly distributed in [0; 1)
// 4. X = 99 / (1 - X)
X = 99 / (1 - X); // 1 - X so there's no chance of div-by-zero
// 5. return max(trunc(X), 100)
const result = Math.floor(X);
return Math.max(1, result / 100);
}
For more reference code, take a look at our
open-source verifier and at this
illustrative demo.