Pages:
Author

Topic: bustabit.com - New seeding event (Read 369 times)

member
Activity: 182
Merit: 11
March 03, 2024, 01:36:10 PM
#21

You need to use the previous hash. So, if prevGameHash is the hash of game #10002812, you have to validate the signature of game #10002813, and so on. I didn't try to run your code, but you probably want to use hexToBytes instead of Uint8Array(Buffer.from(foo)) where you declare prevGameHash and vxSignature.

Yep, you're right, hexToBytes did the trick, thanks a lot! And a last question (I promise it'll be really last one :-) ) : How could I deduct vxSignature for the next (in the reverse manner) hash/game ? return bls.verify(vxSignature, message, VX_PUBKEY); just gives me back true/false like a validation, whether the current signature is correct (or not).

In any case many thanks again, Leo, for your co-operation, I really appreciate it!

No problem at all! Happy to help. Well, it's Vx that gives us that signature, so you need to communicate with its API. For example, in the same verifier, we call getVxSignature (line 12 in https://stackblitz.com/edit/bustabit-verifier?file=src%2Futils%2Fvx.ts), validate it, and then finally use it to generate the game's result.

Ah, I thought this in the meantime (and yes, this make a sense, actually). Anyway, is there API for bustabit.com as well? If I'd get VxSignature from Vx, I can get the game result for specific game directly (provided that I trust the result even without verification via vxSignature, of course :-) ).
copper member
Activity: 106
Merit: 34
bustabit & bustadice
March 03, 2024, 11:27:06 AM
#20

You need to use the previous hash. So, if prevGameHash is the hash of game #10002812, you have to validate the signature of game #10002813, and so on. I didn't try to run your code, but you probably want to use hexToBytes instead of Uint8Array(Buffer.from(foo)) where you declare prevGameHash and vxSignature.

Yep, you're right, hexToBytes did the trick, thanks a lot! And a last question (I promise it'll be really last one :-) ) : How could I deduct vxSignature for the next (in the reverse manner) hash/game ? return bls.verify(vxSignature, message, VX_PUBKEY); just gives me back true/false like a validation, whether the current signature is correct (or not).

In any case many thanks again, Leo, for your co-operation, I really appreciate it!

No problem at all! Happy to help. Well, it's Vx that gives us that signature, so you need to communicate with its API. For example, in the same verifier, we call getVxSignature (line 12 in https://stackblitz.com/edit/bustabit-verifier?file=src%2Futils%2Fvx.ts), validate it, and then finally use it to generate the game's result.
member
Activity: 182
Merit: 11
March 01, 2024, 11:22:03 AM
#19

You need to use the previous hash. So, if prevGameHash is the hash of game #10002812, you have to validate the signature of game #10002813, and so on. I didn't try to run your code, but you probably want to use hexToBytes instead of Uint8Array(Buffer.from(foo)) where you declare prevGameHash and vxSignature.

Yep, you're right, hexToBytes did the trick, thanks a lot! And a last question (I promise it'll be really last one :-) ) : How could I deduct vxSignature for the next (in the reverse manner) hash/game ? return bls.verify(vxSignature, message, VX_PUBKEY); just gives me back true/false like a validation, whether the current signature is correct (or not).

In any case many thanks again, Leo, for your co-operation, I really appreciate it!
copper member
Activity: 106
Merit: 34
bustabit & bustadice
February 29, 2024, 08:22:40 PM
#18
Interesting. I tried to "re-make" second part of code for Node.js and game #10002812, in that way :

Code:
const BLS = require('./node_modules/@noble/curves/bls12-381.js');
const CB = require('./node_modules/@noble/hashes/utils.js');
const U8TB = require('./node_modules/@noble/hashes/utils.js');

const VX_PUBKEY = "b40c94495f6e6e73619aeb54ec2fc84c5333f7a88ace82923946fc5b6c8635b08f9130888dd96e1749a1d5aab00020e4";

// this is prevGameHash for the game #10002812
var prevGameHash1 = "cde36243fc801dc2c77ba6284c81c5fc047039f217e704eb0edafc14e3d1b0a2";
let prevGameHash = new Uint8Array(Buffer.from(prevGameHash1));
  console.log(prevGameHash.length);
  console.log(prevGameHash);
var gameSalt = "000000000000000000011f6e135efe67d7463dfe7bb955663ef88b1243b2deea";
// this is vxSignature for the game #10002812
var vxSignature1 = "8b1ac9ffd6f0594be105c248a56c23e628b088ba4d6c8407c4dd4f406a977a2f68afc83f82a002a36c10df0cd2a3a9430703beec34ae1f218458237c49d39bef0ede106f53d1c3036fef3442d32900af207d03cfe9c46e49ac1d0c304d35ceff";
// this is vxSignature for the game #10002813
//var vxSignature1 = "8f2466a009be4b370d12fe90a9fcc89dbefe1ac08240877a82e16a6e2c0be6a4a0db43bc52dd5eab886fb61dc13d714504af001f4377b0ca2fc269fa3f5fc871c1c61ef6e9f2cd5e862354d5955c0a9c8674dcd7d291a82c448e4f3c68126bad";
let vxSignature = new Uint8Array(Buffer.from(vxSignature1));
  console.log(vxSignature.length);
  console.log(vxSignature);
const message = CB.concatBytes(prevGameHash, U8TB.utf8ToBytes(gameSalt));
  console.log(message.length);
  console.log(message);
return BLS.bls12_381.verify(vxSignature, message, VX_PUBKEY);

When I use vxSignature for the game #10002812, it gives me node_modules/@noble/curves/bls12-381.js:144 throw new Error('No root'); . When I change vxSignature for the game #10002813, it gives me node_modules/@noble/curves/abstract/weierstrass.js:258 throw new Error('bad point: not in prime-order subgroup');.

Just for sure - the length of the prevGameHash is 64 bytes, length of the vxSignature 192 bytes and lenght of the message 128 bytes, correct ? Maybe there will be some issue with the re-code of string to Uint8Array in Node.js ...


You need to use the previous hash. So, if prevGameHash is the hash of game #10002812, you have to validate the signature of game #10002813, and so on. I didn't try to run your code, but you probably want to use hexToBytes instead of Uint8Array(Buffer.from(foo)) where you declare prevGameHash and vxSignature.
member
Activity: 182
Merit: 11
February 29, 2024, 06:42:03 AM
#17
Yeah, thanks, I probably missed it. It just looks like it needs lot of modules/dependencies for a "webrunning", which I don't need, so I'm transferring the code to the Node.JS format (I hope I'm close now, otherwise I'll think about re-writing the code into Python, someday Smiley ).

Just a question to a second part of code (generating vxSignature) :

Code:
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);
}

What is input for the validateSignature function, are they the results from the previous hash ? So, if I'm finding e.g. vxSignature for game #10002813, do I have to find and input here prevGameHash of game #10002812, and the vxSignature of this game as well?

Yeah, that's correct. We concatenate the previous game hash with our game salt, and ask Vx to sign the message. Vx gives us as a signature (which reveals the previous game to Vx) and then what that function does is to verify that the signature is authentic. By the way, there is also a crash demo here, in case you haven't seen it.

Interesting. I tried to "re-make" second part of code for Node.js and game #10002812, in that way :

Code:
const BLS = require('./node_modules/@noble/curves/bls12-381.js');
const CB = require('./node_modules/@noble/hashes/utils.js');
const U8TB = require('./node_modules/@noble/hashes/utils.js');

const VX_PUBKEY = "b40c94495f6e6e73619aeb54ec2fc84c5333f7a88ace82923946fc5b6c8635b08f9130888dd96e1749a1d5aab00020e4";

// this is prevGameHash for the game #10002812
var prevGameHash1 = "cde36243fc801dc2c77ba6284c81c5fc047039f217e704eb0edafc14e3d1b0a2";
let prevGameHash = new Uint8Array(Buffer.from(prevGameHash1));
  console.log(prevGameHash.length);
  console.log(prevGameHash);
var gameSalt = "000000000000000000011f6e135efe67d7463dfe7bb955663ef88b1243b2deea";
// this is vxSignature for the game #10002812
var vxSignature1 = "8b1ac9ffd6f0594be105c248a56c23e628b088ba4d6c8407c4dd4f406a977a2f68afc83f82a002a36c10df0cd2a3a9430703beec34ae1f218458237c49d39bef0ede106f53d1c3036fef3442d32900af207d03cfe9c46e49ac1d0c304d35ceff";
// this is vxSignature for the game #10002813
//var vxSignature1 = "8f2466a009be4b370d12fe90a9fcc89dbefe1ac08240877a82e16a6e2c0be6a4a0db43bc52dd5eab886fb61dc13d714504af001f4377b0ca2fc269fa3f5fc871c1c61ef6e9f2cd5e862354d5955c0a9c8674dcd7d291a82c448e4f3c68126bad";
let vxSignature = new Uint8Array(Buffer.from(vxSignature1));
  console.log(vxSignature.length);
  console.log(vxSignature);
const message = CB.concatBytes(prevGameHash, U8TB.utf8ToBytes(gameSalt));
  console.log(message.length);
  console.log(message);
return BLS.bls12_381.verify(vxSignature, message, VX_PUBKEY);

When I use vxSignature for the game #10002812, it gives me node_modules/@noble/curves/bls12-381.js:144 throw new Error('No root'); . When I change vxSignature for the game #10002813, it gives me node_modules/@noble/curves/abstract/weierstrass.js:258 throw new Error('bad point: not in prime-order subgroup');.

Just for sure - the length of the prevGameHash is 64 bytes, length of the vxSignature 192 bytes and lenght of the message 128 bytes, correct ? Maybe there will be some issue with the re-code of string to Uint8Array in Node.js ...
copper member
Activity: 106
Merit: 34
bustabit & bustadice
February 29, 2024, 12:16:24 AM
#16
Yeah, thanks, I probably missed it. It just looks like it needs lot of modules/dependencies for a "webrunning", which I don't need, so I'm transferring the code to the Node.JS format (I hope I'm close now, otherwise I'll think about re-writing the code into Python, someday Smiley ).

Just a question to a second part of code (generating vxSignature) :

Code:
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);
}

What is input for the validateSignature function, are they the results from the previous hash ? So, if I'm finding e.g. vxSignature for game #10002813, do I have to find and input here prevGameHash of game #10002812, and the vxSignature of this game as well?

Yeah, that's correct. We concatenate the previous game hash with our game salt, and ask Vx to sign the message. Vx gives us as a signature (which reveals the previous game to Vx) and then what that function does is to verify that the signature is authentic. By the way, there is also a crash demo here, in case you haven't seen it.
member
Activity: 182
Merit: 11
February 28, 2024, 09:32:35 PM
#15

Have you seen the new verifier I wrote? It runs in the browser and already does what you're asking for (uses a vxSignature to calculate game results). Take a look at this file to see how you could do it.

Thanks, that TypeScript looks great! Just tell me, please, (from) where should I import ./constants, ./utils/math and ./utils/vx ? Thanks!

You could just copy those from the verifier, they are all there. Just make sure you import the right dependencies and it should work.

Yeah, thanks, I probably missed it. It just looks like it needs lot of modules/dependencies for a "webrunning", which I don't need, so I'm transferring the code to the Node.JS format (I hope I'm close now, otherwise I'll think about re-writing the code into Python, someday Smiley ).

Just a question to a second part of code (generating vxSignature) :

Code:
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);
}

What is input for the validateSignature function, are they the results from the previous hash ? So, if I'm finding e.g. vxSignature for game #10002813, do I have to find and input here prevGameHash of game #10002812, and the vxSignature of this game as well?
copper member
Activity: 106
Merit: 34
bustabit & bustadice
February 28, 2024, 06:49:58 PM
#14

Have you seen the new verifier I wrote? It runs in the browser and already does what you're asking for (uses a vxSignature to calculate game results). Take a look at this file to see how you could do it.

Thanks, that TypeScript looks great! Just tell me, please, (from) where should I import ./constants, ./utils/math and ./utils/vx ? Thanks!

You could just copy those from the verifier, they are all there. Just make sure you import the right dependencies and it should work.
member
Activity: 182
Merit: 11
February 28, 2024, 03:02:29 AM
#13

Have you seen the new verifier I wrote? It runs in the browser and already does what you're asking for (uses a vxSignature to calculate game results). Take a look at this file to see how you could do it.

Thanks, that TypeScript looks great! Just tell me, please, (from) where should I import ./constants, ./utils/math and ./utils/vx ? Thanks!
copper member
Activity: 106
Merit: 34
bustabit & bustadice
February 27, 2024, 09:05:30 PM
#12

E.g. this is "prevHash" for game #7600000 (so I'm able to count all the games from #7600000 till hash of the game as a parameter of this script). So I supposed, when I replace this prevHash with (e.g.) hash of game #10000001, and change salt ("let bust = gameResult" row) to the new one ( 000000000000000000011f6e135efe67d7463dfe7bb955663ef88b1243b2deea), everything will work again. But it is not Wink. So, could you tell me, please, how could I extend this script (vxSignature is necessary?), for properly working again? Thanks a lot!

PS: The script that I mentioned above I successfully ran as "node script.js" from linux command line. Now I'm trying to process your code in the same manner (or like /usr/bin/node script.mhs), but it has probably troubles with the type definition in the function (e.g. hash: Uint8Array, gameSalt: string, etc.), it gives me SyntaxError: Unexpected token ':' error. So, if you'd place example here, how to run this JS script (ideally from the cmd line as well), it'll be really useful.

Have you seen the new verifier I wrote? It runs in the browser and already does what you're asking for (uses a vxSignature to calculate game results). Take a look at this file to see how you could do it.
member
Activity: 182
Merit: 11
February 27, 2024, 12:24:58 PM
#11

E.g. this is "prevHash" for game #7600000 (so I'm able to count all the games from #7600000 till hash of the game as a parameter of this script). So I supposed, when I replace this prevHash with (e.g.) hash of game #10000001, and change salt ("let bust = gameResult" row) to the new one ( 000000000000000000011f6e135efe67d7463dfe7bb955663ef88b1243b2deea), everything will work again. But it is not Wink. So, could you tell me, please, how could I extend this script (vxSignature is necessary?), for properly working again? Thanks a lot!

PS: The script that I mentioned above I successfully ran as "node script.js" from linux command line. Now I'm trying to process your code in the same manner (or like /usr/bin/node script.mhs), but it has probably troubles with the type definition in the function (e.g. hash: Uint8Array, gameSalt: string, etc.), it gives me SyntaxError: Unexpected token ':' error. So, if you'd place example here, how to run this JS script (ideally from the cmd line as well), it'll be really useful.
member
Activity: 182
Merit: 11
February 27, 2024, 03:39:26 AM
#10
In the "previous bustabit", I used this part of code for calculating hash and game result :

Code:
var CryptoJS = require('./node_modules/crypto-js/crypto-js.js');
var isVerifying = false;

  var hash_input = process.argv[2];

  let prevHash = null;
 
  while (prevHash != 'f7a0cc38f223a24f40ca70faa4adb433be79be3eaf9ac8b37da39f57f1f8ed62') {
    let hash = String(prevHash ? CryptoJS.SHA256(String(prevHash)) : hash_input);
    let bust = gameResult(hash, '0000000000000000004d6ec16dafe9d8370958664c1dc422f452892264c59526');
   
    console.log(bust, hash);

    prevHash = hash;
  }

function gameResult(seed, salt) {
  const nBits = 52; // number of most significant bits to use

  // 1. HMAC_SHA256(message=seed, key=salt) 
  const hmac = CryptoJS.HmacSHA256(CryptoJS.enc.Hex.parse(seed), salt);
  seed = hmac.toString(CryptoJS.enc.Hex);

  // 2. r = 52 most significant bits
  seed = seed.slice(0, nBits / 4);
  const r = 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);

  // 5. return max(trunc(X), 100)
  const result = Math.floor(X);
  return Math.max(1, result / 100);
};

E.g. this is "prevHash" for game #7600000 (so I'm able to count all the games from #7600000 till hash of the game as a parameter of this script). So I supposed, when I replace this prevHash with (e.g.) hash of game #10000001, and change salt ("let bust = gameResult" row) to the new one ( 000000000000000000011f6e135efe67d7463dfe7bb955663ef88b1243b2deea), everything will work again. But it is not Wink. So, could you tell me, please, how could I extend this script (vxSignature is necessary?), for properly working again? Thanks a lot!
newbie
Activity: 4
Merit: 0
February 26, 2024, 02:55:25 PM
#9
What about bustadice's new seeding event? Can you send me a link to the forum talking about that or can you post all of the particulars as you did here for Bustabit.com about Bustadice.com please? Thank you!

bustadice doesn't require one. While on bustabit, game results come from a hash-chain, bustadice calculates them with input from the player, the game server, and the auditor. The only change is that ActuallyFair.com will be the new auditor; however, aside from that, the game will remain largely the same. You can find more information here.
 


The Bitcoin block 831500 has been mined! Our game salt is: 000000000000000000011f6e135efe67d7463dfe7bb955663ef88b1243b2deea.


So, basically everything will remain identical except for a new hash chain? Any plans to change any game limits or betting functionality?
copper member
Activity: 106
Merit: 34
bustabit & bustadice
February 21, 2024, 07:29:15 PM
#8
What about bustadice's new seeding event? Can you send me a link to the forum talking about that or can you post all of the particulars as you did here for Bustabit.com about Bustadice.com please? Thank you!

bustadice doesn't require one. While on bustabit, game results come from a hash-chain, bustadice calculates them with input from the player, the game server, and the auditor. The only change is that ActuallyFair.com will be the new auditor; however, aside from that, the game will remain largely the same. You can find more information here.
 


The Bitcoin block 831500 has been mined! Our game salt is: 000000000000000000011f6e135efe67d7463dfe7bb955663ef88b1243b2deea.
newbie
Activity: 2
Merit: 0
February 20, 2024, 07:45:14 PM
#7
What about bustadice's new seeding event? Can you send me a link to the forum talking about that or can you post all of the particulars as you did here for Bustabit.com about Bustadice.com please? Thank you!
newbie
Activity: 18
Merit: 3
February 19, 2024, 01:11:09 PM
#6
so the house edge is still 1%?

Yeah, you are able to verify this by analyzing the by the "gameResult" function
newbie
Activity: 15
Merit: 0
February 19, 2024, 11:49:45 AM
#5
so the house edge is still 1%?
newbie
Activity: 18
Merit: 3
February 19, 2024, 11:48:21 AM
#4
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:
Code:
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:
Code:
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:

Code:
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:
Code:
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.
copper member
Activity: 106
Merit: 34
bustabit & bustadice
sr. member
Activity: 528
Merit: 368
February 19, 2024, 10:35:22 AM
#2
Quoting to prevent editing of the OP:

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:
Code:
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:
Code:
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:

Code:
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:
Code:
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.
Pages:
Jump to: