Case battles use the EOS blockchain which allows us to generate a result in the future. On the EOS blockchain, a new block is mined every few seconds, so we can reasonably assume a new one is going to be mined a few seconds after a case battle starts and we can use this, along with other variables, to generate a result.
When you create a battle, no result is generated until all other player or bots joins. Once all players and/or bots have joined, an EOS block index is generated. That index can be used by users to verify the timestamp that the EOS block has been hashed. There are a lot of blockchain websites that can verify this. We use bloks.io but there are other options available from here: https://eosnetwork.com/block-explorers/. Once you select the provider, you can verify that the block was indeed some seconds into the future. An example block can be found here: https://eosauthority.com/block/405795568.
This method helps customers ensure that no one knows what the result will be as that would require you to be able to see into the future.
In total, there are 5 variables that are used for result generation:
Server seed (A randomly generated string assigned when a battle is created - Example: 8aac0df7de35e755de14d0f9f6733a2d)
EOS Block Seed (The ID of an EOS block, which is to be generated after a battle is joined by all players/bots - Example: 157693bfa9ac7f396738ff4d19d7e200efc04cfa6183736f2d6f39657757895f)
Round number (The round of the spin you're verifying - Example: 2)
Player Position (Where the player you're verifying the spin for appears in the battle from left to right. If you appear 2nd from the left, your position would be 2)
Ticket Quantity (The amount of tickets in the case you're verifying. For all current cases, this is 100,000)
To verify previous results, customers can use the following PHP script:
<?php
//Case Battle Fairness | Round Validation
$serverSeed = '-1'; // Server seed
$eosBlockSeed = '-1'; // EOS Block Seed
/*
* This is the round number, where 1 round is one case opened. If the battle has 10 cases,
* this means there are 10 rounds - 1-10.
* IMPORTANT: If it's the tiebreaker round, it will always be totalRounds + 1
* because the tiebreaker is considered as one additional round. If you have 2 cases
* which results in 2 rounds, the tiebreaker roundNumber is 3 because it's total (2) + 1.
*/
$roundNumber = -1;
/*
* Player position that we want to validate the roll for.
* For example, if you want to validate your own roll and you were holding
* the 3rd position from left to right, then playerPosition will be 3.
*/
$playerPosition = -1; // The Player Position (1 to 4)
// Update this to true if you are validating a tiebreaker result.
$isTiebreaker = false;
/*
* Case Ticket's Quantity, by default it's 100,000
* We only change this if there's a case that has different ticket distribution.
* Right now, all cases use 100,000 by default.
* IMPORTANT: If it's the tiebreaker round, this will be number of players participating on the tiebreaker.
* For example, if two players are participating on a tiebreaker the ticketQuantity is 2.
*/
$ticketQuantity = 100000;
/* ------------------ */
if ($serverSeed == '' || $eosBlockSeed == '' || $roundNumber < 0 || $playerPosition < 1 || $playerPosition > 4 || $ticketQuantity <= 0) {
echo "Fill in details";
return;
}
define('MAX_HEX_SEGMENTS', 6);
define('HEX_SEGMENT_SIZE', 2);
define('BASE_FOR_HEX_CONVERSION', 256);
define('HASH_TYPE', 'sha256');
function calculateDecimalValue(string $preResult): float
{
$decimalValue = 0;
for ($i = 0; $i < MAX_HEX_SEGMENTS; $i++) {
$hexValue = substr($preResult, HEX_SEGMENT_SIZE * $i, HEX_SEGMENT_SIZE);
$decimalValue += hexdec($hexValue) / pow(BASE_FOR_HEX_CONVERSION, $i + 1);
}
return $decimalValue;
}
function getProvablyFairResult(string $init, string $serverSeed, int $qty): array
{
$preResult = hash_hmac(HASH_TYPE, $init, $serverSeed);
$decimalValue = calculateDecimalValue($preResult);
$result = (int) ($decimalValue * $qty) + 1;
return [
'preResult' => $preResult,
'result' => $result,
];
}
$serverSeed = preg_replace("/\r|\n/", "", $serverSeed);
$eosBlockSeed = preg_replace("/\r|\n/", "", $eosBlockSeed);
if ($isTiebreaker) {
$stringToHash = "$eosBlockSeed-$roundNumber";
} else {
$stringToHash = "$eosBlockSeed-$roundNumber-$playerPosition";
}
$result = getProvablyFairResult($stringToHash, $serverSeed, $ticketQuantity);
echo "Result: {$result['result']}";
?>
Openings before April 18th 2024 used this code: https://3v4l.org/SCdP9
This can be run locally or on any PHP script runner found online. We direct customers to: https://3v4l.org/ for this, but there are plenty of options available like so:
Customers will need to edit the serverSeed, eosBlockSeed, roundNumber, playerPosition & ticketQuantity variables in the script to verify results. In case you are verifying the tiebreaker, you also need to change the ‘$isTiebreaker' variable to true, that is because when we’re dealing with tiebreakers, the player position is irrelevant, hence we ignore it.
This information can be found here: https://csgoempire.com/fairness under ‘Case Battles’.
For example, if we run the following script it matches up with the result logged in my case battles history for battle 1013463:
<?php
//Case Battle Fairness | Round Validation
$serverSeed = 'ab4b631d02e718e538848a5e9a023216'; // Server seed
$eosBlockSeed = '15f6de04a2d27291b6a07e547edfec57a02fdbff6e3e26d1761c1f2b652595f4'; // EOS Block Seed
/*
* This is the round number, where 1 round is one case opened. If the battle has 10 cases,
* this means there are 10 rounds - 1-10.
* IMPORTANT: If it's the tiebreaker round, it will always be totalRounds + 1
* because the tiebreaker is considered as one additional round. If you have 2 cases
* which results in 2 rounds, the tiebreaker roundNumber is 3 because it's total (2) + 1.
*/
$roundNumber = 1;
/*
* Player position that we want to validate the roll for.
* For example, if you want to validate your own roll and you were holding
* the 3rd position from left to right, then playerPosition will be 3.
*/
$playerPosition = 1; // The Player Position (1 to 4)
// Update this to true if you are validating a tiebreaker result.
$isTiebreaker = false;
/*
* Case Ticket's Quantity, by default it's 100,000
* We only change this if there's a case that has different ticket distribution.
* Right now, all cases use 100,000 by default.
* IMPORTANT: If it's the tiebreaker round, this will be number of players participating on the tiebreaker.
* For example, if two players are participating on a tiebreaker the ticketQuantity is 2.
*/
$ticketQuantity = 100000;
/* ------------------ */
if ($serverSeed == '' || $eosBlockSeed == '' || $roundNumber < 0 || $playerPosition < 1 || $playerPosition > 4 || $ticketQuantity <= 0) {
echo "Fill in details";
return;
}
define('MAX_HEX_SEGMENTS', 6);
define('HEX_SEGMENT_SIZE', 2);
define('BASE_FOR_HEX_CONVERSION', 256);
define('HASH_TYPE', 'sha256');
function calculateDecimalValue(string $preResult): float
{
$decimalValue = 0;
for ($i = 0; $i < MAX_HEX_SEGMENTS; $i++) {
$hexValue = substr($preResult, HEX_SEGMENT_SIZE * $i, HEX_SEGMENT_SIZE);
$decimalValue += hexdec($hexValue) / pow(BASE_FOR_HEX_CONVERSION, $i + 1);
}
return $decimalValue;
}
function getProvablyFairResult(string $init, string $serverSeed, int $qty): array
{
$preResult = hash_hmac(HASH_TYPE, $init, $serverSeed);
$decimalValue = calculateDecimalValue($preResult);
$result = (int) ($decimalValue * $qty) + 1;
return [
'preResult' => $preResult,
'result' => $result,
];
}
$serverSeed = preg_replace("/\r|\n/", "", $serverSeed);
$eosBlockSeed = preg_replace("/\r|\n/", "", $eosBlockSeed);
if ($isTiebreaker) {
$stringToHash = "$eosBlockSeed-$roundNumber";
} else {
$stringToHash = "$eosBlockSeed-$roundNumber-$playerPosition";
}
$result = getProvablyFairResult($stringToHash, $serverSeed, $ticketQuantity);
echo "Result: {$result['result']}";
?>
If the battle went to a tiebreaker (players had unboxed the same amount by the end of the battle) we can edit the script to verify that this tiebreaker was also provably fair.
To do this we'll need to:
Change 'isTiebreaker' to 'true'
Change 'roundNumber' to +1 round from the total rounds in the battle. In this battle, there was 1 round, so we'd use 2.
Change 'ticketQuantity' to the number of players participating in the tiebreak. Your position in the tiebreaker is in ascending order. For example, if 3 people are in a tiebreaker, the assigned ticket goes from left to right. This means that if players in position 2 and 3 are in a tiebreaker, ticket number 1 is assigned to player 2 and ticket number 2 is assigned to player 3.
This helps prove to customers that we generated the same result as what was given out. If the provably fair script returned a different result, it would be obvious that we manipulated the result compared to what was given out to customers.