So funktioniert Provably Fair bei Pump

So spielst du Pump auf Cloudbet, und so überprüfst du, dass die fehlerhafte Pumpe jeder Runde provably fair ist.

So spielst du Pump

Pump ist ein Risikospiel: Du pumpst etwas auf (in Cloudbets Version einen Ballon), das mit jeder Pumpe einen Multiplikator wachsen lässt. Cashe jederzeit aus, um deinen Gewinn zu sichern — gehst du jedoch zu weit, platzt der Ballon, und du verlierst deinen Einsatz. Bevor du startest, wählst du in der Regel ein Schwierigkeitsniveau, das abwägt, wie weit du sicher gehen kannst, gegen die Geschwindigkeit, mit der der Multiplikator wächst.

Sobald du deine Wette platziert hast, drücke Pumpen, um den Ballon Schritt für Schritt aufzublasen. Jede erfolgreiche Pumpe erhöht deine mögliche Auszahlung. Du kannst nach jeder Pumpe auf Auszahlen drücken, um deinen aktuellen Multiplikator zu kassieren — oder weiter pumpen für einen größeren. Ist deine nächste Pumpe zufällig die fehlerhafte der Runde, platzt der Ballon sofort, und die Runde endet ohne Auszahlung.

Cloudbet Originals Pump

Cloudbets eigenes Originals Pump hat fünf Schwierigkeitsstufen — Easy, Medium, Hard, Expert und Master —, die steuern, wie viele der 25 Pumpen einer Runde „fehlerhaft" sind (also den Ballon zum Platzen bringen). Es hat einen RTP von 99 %, einen maximalen Gewinn von 1.000.000 $ und akzeptiert Einsätze ab 0,01 $. Der Autobet-Modus lässt dich Einsatzhöhe, Pumpen pro Runde, Gewinn-/Verlustlimits und die Anzahl automatisch zu spielender Runden konfigurieren — manuelles Spiel („Pump wartet auf DEINEN Input") steht ebenfalls immer zur Verfügung.

Für die allgemeine Mechanik von Provably Fair — Server-Seed, Client-Seed, Nonce, Hashing und wie die Round Signature erstellt wird — siehe Cloudbets Provably-Fair-Leitfaden. Im Folgenden erfährst du, wie diese Zufallszahlenfolge speziell zum Ergebnis einer Pump-Runde wird.

Zufallszahlen werden zu einem Spiellayout

Die 25 Pumpen von Pump sind mit A bis Y beschriftet, damit wir sie verfolgen können:

Diagramm, das die 25 Pumpen von Pump zeigt, beschriftet von A bis Y, mit denen Cloudbets nachweisbar faire Auswahl der defekten Pumpe nachverfolgt wird.

Zu Beginn sind alle Pumpen nicht fehlerhaft. Mit der ersten Zahl der Zufallszahlenfolge der Runde wählen wir eine nicht fehlerhafte Pumpe per Index aus und machen sie fehlerhaft:

Praktisches Beispiel für den nachweisbar fairen Algorithmus von Pump: Die erste defekte Pumpe wird zufällig aus allen 25 funktionierenden Pumpen ausgewählt.

Diese Pumpe wird dann aus der Liste der nicht fehlerhaften Pumpen entfernt, sodass die nächste Auswahl nur noch aus den verbleibenden erfolgt:

Diagramm, das zeigt, wie Cloudbets nachweisbar faires Pump-System eine ausgewählte defekte Pumpe aus der Liste entfernt, bevor die nächste gezogen wird.

Das wiederholt sich, bis die Anzahl fehlerhafter Pumpen für die Schwierigkeitsstufe der Runde erreicht ist (hier gezeigt: Medium = 3):

Praktisches Beispiel, das zeigt, wie der nachweisbar faire Algorithmus von Pump eine zweite defekte Pumpe aus den verbleibenden funktionierenden Pumpen auswählt.

Sobald jede fehlerhafte Pumpe der Runde ausgewählt ist, werden die zur Auswahl verwendeten Zahlen nicht mehr benötigt — übrig bleibt nur, welche Pumpen fehlerhaft sind:

Diagramm, das das vollständige Layout der defekten Pumpen einer Runde zeigt, bevor Cloudbets nachweisbar faire Berechnung es auf die eine relevante Pumpe vereinfacht.

Da Pumpen der Reihe nach ausgelöst werden (A, dann B, dann C…), zählt nur die erste fehlerhafte Pumpe, die der Spieler erreicht — alles danach wird nie ausgelöst. Das Layout vereinfacht sich also auf genau diese eine relevante Pumpe:

Diagramm, das das nachweisbar faire Layout der defekten Pumpen von Pump zeigt, vereinfacht auf nur die erste defekte Pumpe, die der Spieler tatsächlich erreichen würde.

Technische Details

Wir nennen die Anzahl der nicht fehlerhaften Pumpen P. Die Zufallsbytes von SHAKE256 werden in vorzeichenlose 4-Byte-Ganzzahlen aufgeteilt (0 bis 2³²−1). Um fair auszuwählen, welche Pumpe fehlerhaft wird, verwenden wir Rejection Sampling gegen P und nehmen dann den Rest (+1) als Index der nicht fehlerhaften Pumpe, die fehlerhaft gemacht wird. Werden weitere fehlerhafte Pumpen benötigt, wird die gewählte Pumpe aus der Liste entfernt, P wird um 1 verringert, und eine neue Zahl wird gezogen — wiederholt, bis die Anzahl fehlerhafter Pumpen der Runde erreicht ist.

Das endgültige Layout der fehlerhaften Pumpen wird serverseitig generiert und gespeichert, bevor die Runde beginnt, und kann danach nicht verändert werden, ohne das bereits an den Spieler übermittelte Commitment zu brechen. Jede Pumpe, die der Spieler drückt, wird mit diesem bereits festgelegten Layout abgeglichen.

Überprüfe es selbst

Wir haben ein kleines Node.js-Programm geschrieben, das exakt Cloudbets Pump-Implementierung entspricht — gib deinen Server-Seed, Client-Seed, Nonce und dein Schwierigkeitsniveau ein, um das Ergebnis deiner Runde zu reproduzieren. (Hinweis: Um den Server-Seed einer Runde auf Cloudbet offenzulegen, musst du bereits zu einem neuen Seed-Paar rotiert haben.)

// If you are running this yourself, then you can input your own data here
// You can then compare:
// 1) Cloudbet's provided Commitment to the Commitment outputted here. This proves that the Server Seed was not altered during gameplay.
// 2) Cloudbet's provided game outcome to the game outcome outputted here. This proves that your game's outcome was determined solely by the pre-game seeds and was not altered afterwards.
let myServerSeed      = 'your_server_seed'; // ✍️YOUR_INPUT✍️: Enter your (unhashed) Server Seed here
let myClientSeed      = 'your_client_seed'; // ✍️YOUR_INPUT✍️: Enter your Client Seed here
let myNonce           = 1;                  // ✍️YOUR_INPUT✍️: Enter your Nonce here
let myDifficultyLevel = 'easy';             // ✍️YOUR_INPUT✍️: Enter your difficulty level here (easy, medium, hard, expert, or master)
const usingCustomData = false;              // Change this to true if you are using your own data

// These are the possible difficulty levels (and how many faulty pumps there are per difficulty level)
const faultyPumpsPerDiff = {
  'easy':       1,
  'medium':     3,
  'hard':       5,
  'expert':     9,
  'master':     12
};

// Load libraries
const crypto = require('crypto');
const { sha3_256, shake256 } = require('js-sha3');

// Step 1: Generate a Server Seed and Commitment
// And generate a Client Seed - either by player input or by the player's browser
function generateServerSeed() {
  let seed = crypto.randomBytes(32).toString('hex');
  if (usingCustomData) { seed = myServerSeed; }
  const commitment = sha3_256(seed);
  return { 'serverSeed': seed, 'commitment': commitment };
}
function getClientSeed() {
  let seed = crypto.randomBytes(32).toString('hex');
  if (usingCustomData) { seed = myClientSeed; }
  return seed;
}

// Step 2: Create a Round Signature with SHA3 using a Server Seed, Client Seed and Nonce 
function createRoundSignature(serverSeed, clientSeed, nonce) {
  return sha3_256(`${serverSeed}:${clientSeed}:${nonce}`);
}

// Step 3: Create a SHAKE256 stream generator from a Round Signature. 
// To simplify this example program, this function doesn't create a stream, but instead a sequence of custom length.
function* createRandomNumberSequence(numbersToGenerate, roundSignature) {
  const numOfBytes = 4 * numbersToGenerate;           // 4 bytes (32 bits) per random number we generate
  const byteStream = shake256.create(8 * numOfBytes); // shaker256 takes bits as input
  byteStream.update(roundSignature);                  // Use the Round Signature to seed the generator
  const buf = Buffer.from(byteStream.digest());       // The Buffer class lets us package every 4 bytes into one 32-bit integer
  for (let i = 0; i < numbersToGenerate; i++) {
    yield buf.readUInt32BE(i * 4);                    // Read buf in 4-byte steps
  }
  throw new Error("No more numbers in the random number sequence! Fix: make 'numbersToGenerate' larger.");
}

// Step 4: Generate indices of the faulty pumps
function pickUniquePositions(numOfFaultyPumps, totalPumps, rng) {
  const available = Array.from({ length: totalPumps }, (_, i) => i);
  const faultyPumps = [];

  for (let i = 0; i < numOfFaultyPumps; ++i) {
    const index = sample(available.length, rng);

    faultyPumps.push(available[index]);
    available.splice(index, 1); // Remove chosen pump from list of non-faulty pumps
  }
  return faultyPumps;
}
// Rejection sampling for fair results
function sample(P, rng) {
  const maxAcceptable = Math.floor(0x100000000 / P) * P;

  // Rejection sampling
  let rand;
  do {
    rand = rng.next().value;
  } while (rand >= maxAcceptable);
  // Modulo operation
  const sampled_number = rand % P;

  return sampled_number;
}

// Example usage
(function main() {
  const totalPumps = 25;
  let difficultyLevel = 'easy';
  let nonce = 1;
  if (usingCustomData) { difficultyLevel = myDifficultyLevel; nonce = myNonce; }

  const numOfFaultyPumps = faultyPumpsPerDiff[difficultyLevel];

  const { serverSeed, commitment } = generateServerSeed();
  const clientSeed = getClientSeed();

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = 12 * 2;                 // We have at most 12 faulty pumps, however due to rejection sampling we may need more numbers
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);

  const faultyPumps = pickUniquePositions(numOfFaultyPumps, totalPumps, rng);
  let firstFaultyPump = Math.min(...faultyPumps);  // Select the smallest/first faulty pump
  firstFaultyPump += 1;                            // Increment the index so that it starts with 1, 2, 3, ... instead of 0, 1, 2

  console.log('📝 Commitment (shown before the game):', commitment);
  console.log('🔒 Server Seed (revealed after the game):', serverSeed);
  console.log('🎲 Client Seed:', clientSeed);
  console.log('🔢 Nonce:', nonce);
  console.log('🔁 Round Signature (can be verified after the game):', roundSignature);
  console.log('🎯 Difficulty level:', difficultyLevel);
  console.log('💣 First faulty pump (zero-indexed, i.e., 1, 2, 3, ...):', firstFaultyPump);
})();

Installiere zunächst die eine Abhängigkeit:

npm install js-sha3

Weitere Provably-Fair-Spielerklärungen:

  1. Provably Fair Mines
  2. Provably Fair Plinko
  3. Provably Fair Dice
  4. Provably Fair Dice: Golden Ticket
  5. Provably Fair Limbo
  6. Provably Fair Roulette
  7. Provably Fair Keno