Comment le provably fair fonctionne dans Pump
Voici comment jouer à Pump sur Cloudbet, et comment vérifier que la pompe défectueuse de chaque manche est provably fair.
Comment jouer à Pump
Pump est un jeu où il faut savoir s'arrêter à temps : vous gonflez quelque chose (un ballon dans la version de Cloudbet) qui fait croître un multiplicateur à chaque pompage. Encaissez à tout moment pour sécuriser vos gains — mais si vous poussez trop loin, il éclate, et vous perdez votre mise. Avant de commencer, vous choisissez généralement un niveau de difficulté qui équilibre jusqu'où vous pouvez aller sans risque par rapport à la vitesse de croissance du multiplicateur.
Une fois votre pari placé, appuyez sur Pump pour gonfler le ballon étape par étape. Chaque pompage réussi augmente votre gain potentiel. Vous pouvez appuyer sur Encaisser après n'importe quel pompage pour encaisser votre multiplicateur actuel — ou continuer à pomper pour un multiplicateur plus élevé. Si votre prochain pompage s'avère être celui qui est défectueux pour cette manche, le ballon éclate immédiatement et la manche se termine sans gain.
Cloudbet Originals Pump
Le Pump de Cloudbet Originals propose cinq niveaux de difficulté — Facile, Moyen, Difficile, Expert et Maître — qui contrôlent combien des 25 pompages de la manche sont « défectueux » (ceux qui font éclater le ballon). Il a un RTP de 99 %, un gain maximal de 1 000 000 $, et accepte des mises à partir de 0,01 $. Le mode Autobet vous permet de configurer votre montant de mise, le nombre de pompages par manche, vos limites de profit/perte, et le nombre de manches à jouer automatiquement — le jeu manuel (« Pump attend VOTRE saisie ») reste toujours disponible.
Pour la mécanique générale du provably fair — source du serveur, source du client, nonce, hachage, et comment la Signature de manche est créée — consultez le guide provably fair de Cloudbet. Voici comment cette séquence de nombres aléatoires devient spécifiquement le résultat d'une manche de Pump.
Transformer des nombres aléatoires en configuration de jeu
Les 25 pompages de Pump sont étiquetés de A à Y afin que nous puissions les suivre :

Tous les pompages commencent non défectueux. En utilisant le premier nombre de la séquence aléatoire de la partie, nous choisissons un pompage non défectueux par indice et le rendons défectueux :

Ce pompage est ensuite retiré de la liste des pompages non défectueux, de sorte que la sélection suivante ne porte plus que sur ce qu'il reste :

Cela se répète jusqu'à ce que le nombre de pompages défectueux du niveau de difficulté de la manche soit atteint (moyen = 3 illustré ici) :

Une fois tous les pompages défectueux de la manche sélectionnés, les nombres utilisés pour les choisir ne sont plus nécessaires — il ne reste plus que la question de savoir quels pompages sont défectueux :

Comme les pompages se déclenchent un par un, dans l'ordre (A, puis B, puis C…), seul le premier pompage défectueux atteint par le joueur compte réellement — tout ce qui suit n'est jamais déclenché. La configuration se simplifie donc à ce seul pompage pertinent :

Détails techniques
Nous appelons P le nombre de pompages non défectueux. Les octets aléatoires issus de SHAKE256 sont divisés en entiers non signés de 4 octets (0 à 2³²−1). Pour choisir équitablement quel pompage est défectueux, nous utilisons l'échantillonnage par rejet sur P, puis nous prenons le reste (+1) comme indice du pompage non défectueux à rendre défectueux. Si d'autres pompages défectueux sont encore nécessaires, le pompage sélectionné est retiré de la liste, P est réduit de 1, et un nouveau nombre est échantillonné — cette opération se répète jusqu'à ce que le nombre de pompages défectueux de la manche soit atteint.
La configuration finale des pompages défectueux est générée et enregistrée côté serveur avant le début de la manche, et ne peut pas être modifiée par la suite sans rompre l'Engagement déjà partagé avec le joueur. Chaque pompage effectué par le joueur est comparé à cette configuration pré-engagée.
Vérifiez-le vous-même
Nous avons écrit un petit programme Node.js correspondant exactement à l'implémentation de Pump chez Cloudbet — entrez votre Source du serveur, votre Source du client, votre Nonce et le niveau de difficulté pour reproduire le résultat de votre manche. (Remarque : pour révéler la Source du serveur d'une manche sur Cloudbet, vous devez avoir déjà renouvelé la paire de sources.)
// 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);
})();
Installez d'abord l'unique dépendance :
npm install js-sha3
