Como o provably fair funciona no Pump
Veja como jogar Pump na Cloudbet, e como verificar se a bomba com defeito de cada rodada é provably fair.
Como jogar Pump
Pump é um jogo de "arrisque até onde der": você infla algo (um balão, na versão da Cloudbet) que aumenta um multiplicador a cada bombeada. Saque a qualquer momento para garantir seus ganhos — mas se você for longe demais, o balão estoura, e você perde sua aposta. Antes de começar, você normalmente escolhe um nível de dificuldade que equilibra até onde é seguro ir contra a rapidez com que o multiplicador cresce.
Depois de fazer sua aposta, pressione Pump para inflar o balão um passo de cada vez. Cada bombeada bem-sucedida aumenta seu prêmio potencial. Você pode pressionar Sacar depois de qualquer bombeada para receber seu multiplicador atual — ou continuar bombeando em busca de um maior. Se a próxima bombeada for a defeituosa da rodada, o balão estoura imediatamente e a rodada termina sem pagamento.
Cloudbet Originals Pump
O Pump Original da Cloudbet tem cinco níveis de dificuldade — Fácil, Médio, Difícil, Especialista e Mestre — que controlam quantas das 25 bombeadas da rodada são "defeituosas" (as que estouram o balão). Tem RTP de 99%, prêmio máximo de $1.000.000, e aceita apostas a partir de $0,01. O Modo Autobet permite configurar o valor da sua aposta, bombeadas por rodada, limites de lucro/perda, e número de rodadas para jogar automaticamente — o modo manual ("Pump espera pelo SEU comando") também está sempre disponível.
Para a mecânica geral do provably fair — seed do servidor, seed do cliente, nonce, hashing, e como a Assinatura da Rodada é criada — veja o guia provably fair da Cloudbet. Abaixo está como essa sequência de números aleatórios se transforma especificamente no resultado de uma rodada de Pump.
Transformando números aleatórios em um layout de jogo
As 25 bombeadas do Pump são rotuladas de A a Y para que possamos rastreá-las:

Todas as bombeadas começam sem defeito. Usando o primeiro número na sequência de números aleatórios do jogo, escolhemos uma bombeada sem defeito por índice e a tornamos defeituosa:

Essa bombeada é então removida da lista de bombeadas sem defeito, então a próxima seleção considera apenas o que restou:

Isso se repete até que o número de bombeadas defeituosas do nível de dificuldade da rodada seja atingido (médio = 3 mostrado aqui):

Assim que todas as bombeadas defeituosas da rodada são escolhidas, os números usados para selecioná-las não são mais necessários — o que resta é apenas quais bombeadas são defeituosas:

Como as bombeadas são acionadas uma de cada vez, em ordem (A, depois B, depois C…), apenas a primeira bombeada defeituosa que o jogador alcança realmente importa — qualquer coisa depois dela nunca é acionada. Então o layout se simplifica para apenas essa bombeada relevante:

Detalhes técnicos
Chamamos o número de bombeadas sem defeito de P. Os bytes aleatórios do SHAKE256 são divididos em inteiros sem sinal de 4 bytes (0 a 2³²−1). Para escolher de forma justa qual bombeada está defeituosa, usamos amostragem por rejeição contra P, e então usamos o resto (+1) como o índice da bombeada sem defeito a ser tornada defeituosa. Se ainda forem necessárias mais bombeadas defeituosas, a bombeada selecionada é removida da lista, P é reduzido em 1, e um novo número é amostrado — repetindo-se até que o número de bombeadas defeituosas da rodada seja atingido.
O layout final de bombeadas defeituosas é gerado e salvo no lado do servidor antes do início da rodada, e não pode ser alterado depois sem quebrar o Compromisso já compartilhado com o jogador. Cada bombeada que o jogador pressiona é verificada contra esse layout pré-comprometido.
Verifique você mesmo
Escrevemos um pequeno programa em Node.js fiel à implementação de Pump na Cloudbet — insira sua Seed do Servidor, Seed do Cliente, Nonce e nível de dificuldade para reproduzir o resultado da sua rodada. (Observação: para revelar a Seed do Servidor de uma rodada na Cloudbet, você precisa já ter rotacionado para um novo par de seeds.)
// 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);
})();
Instale a única dependência primeiro:
npm install js-sha3
