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:

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:

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

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

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:

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:

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
