Block-Sena Whitepaper Técnico
Seguridad y Transparencia

Sorteo auditable, on-chain y verificable por cualquier persona

En Block-Sena, el sorteo usa un flujo commit-reveal en dos fases, combinado con blockhash futuro y prevrandao. Esto hace que el resultado sea determinista después de la ejecución, auditable en blockchain y resistente a la manipulación manual de los números sorteados.

6 de 30 Juego base con 6 números únicos y sorteo de 6 números únicos
6 a 10 Apuesta múltiple con cuotas combinatorias (4/5/6 aciertos)
Multi-Token Payout por token con liquidación independiente y carry-over auditable

Visión General

Block-Sena es una lotería on-chain en la que la compra de boletos, el bloqueo de la ronda, el sorteo y la distribución de premios quedan registrados en la blockchain. El protocolo usa un flujo de dos fases: blockSenaPauseDraw (bloquea la ronda y registra el compromiso) y blockSenaDrawPay (revela, sortea y paga).

Objetivo

Garantizar un proceso auditable, reproducible y con reglas de payout transparentes para cada ronda.

Auditabilidad

Cualquier persona puede verificar inputs (commit, salt, bloque objetivo), cálculo del seed, números sorteados y eventos de liquidación.

Commit-Reveal (2 Fases)

Fase 1: Pausa + Commit

La función blockSenaPauseDraw(usdValuePoolSnapshot, commitHash) bloquea la ronda para nuevas compras, define el bloque objetivo futuro y almacena solo el hash del salt secreto.

Fase 2: Reveal + Draw + Pay

La función blockSenaDrawPay(salt) valida que keccak256(salt) == commitHash, usa el blockhash del bloque objetivo, genera los 6 números únicos y ejecuta los payouts.

Ventana técnica del blockhash

El blockhash solo puede leerse por ~256 bloques. Si expira, la ronda necesita un reset operativo. Este reset es un fail-safe raro, con timeout on-chain, y todo uso queda público on-chain.

Compromiso del Salt

commitHash = keccak256(abi.encodePacked(salt))
El salt no se revela en la fase 1. Solo el hash es público. En la fase 2, el salt revelado debe coincidir exactamente con el commit.

Bloque Objetivo

targetBlock = block.number + 5
El sorteo usa el hash de un bloque futuro. En el momento del commit, ese blockhash aún no existe, por lo que nadie conoce ese input con anticipación.

Flujo del Sorteo (21h UTC -> +5 bloques -> DrawPay)

El flujo operativo de Block-Sena separa claramente el cierre de apuestas de la generación de los 6 números. Esta separación elimina el riesgo de comprar boletos después de conocer el resultado o de intentar ajustar boletos para “acertar” los números sorteados.

1) 21:00 UTC (ventana operativa) -> blockSenaPauseDraw

La función blockSenaPauseDraw(usdValuePoolSnapshot, commitHash) bloquea la ronda, bloquea nuevas compras, registra el commitHash, define targetBlock = block.number + 5 y guarda el snapshot del pool en USD.

2) Apuestas congeladas antes de que exista la aleatoriedad

Después del PauseDraw, ningún nuevo boleto entra en esa ronda. En ese momento, el blockhash(targetBlock) aún no existe, por lo que los 6 números finales todavía no pueden ser previstos ni calculados.

3) +5 bloques (después del bloque objetivo) -> blockSenaDrawPay

La función blockSenaDrawPay(salt) valida el salt contra el commit, lee el blockhash del bloque objetivo, genera los 6 números únicos y ejecuta los payouts por token. Si se intenta llamar antes del bloque objetivo, la transacción revierte on-chain.

4) Auditoría pública en cualquier momento

Cualquier persona puede verificar en el explorer: tx de blockSenaPauseDraw, targetBlock, tx de blockSenaDrawPay, salt revelado, números sorteados y eventos de liquidación/pago.

Qué evita este diseño en la práctica

No es posible comprar un boleto después de ver los 6 números de la ronda, porque la ronda ya fue bloqueada en blockSenaPauseDraw antes de ejecutar el sorteo.

No es posible conocer los 6 números en el momento de la pausa, porque la fórmula depende de un bloque futuro (targetBlock) que aún no existe en ese momento.

No es posible ejecutar el sorteo antes de la hora técnica, porque blockSenaDrawPay exige on-chain que el bloque actual ya haya pasado el targetBlock.

Sobre ResetDraw (escenario raro)

blockSenaResetDraw existe solo para evitar que una ronda quede bloqueada para siempre si ocurre una falla larga de infraestructura (ej.: RPCs indisponibles, mantenimiento de servicios, caída de red/infra o problemas operativos prolongados) y DrawPay no puede ejecutarse antes de la expiración de blockhash(targetBlock).

En el flujo normal, lo esperado es ejecutar DrawPay pocos bloques después del PauseDraw (objetivo en +5 bloques). El reset es una protección de liveness, no parte del flujo normal del sorteo.

Fórmula Matemática del Sorteo (6 números únicos)

El seed del sorteo se deriva de la combinación de un bloque futuro, del salt comprometido y de datos de la ronda. A partir de él, el contrato genera números del 1 al 30, descartando duplicados hasta completar 6 decenas únicas.

En términos de seguridad operativa: como los boletos ya fueron congelados en la fase de PauseDraw antes de que exista ese bloque futuro, el sorteo no puede ser “armado” para coincidir con un boleto insertado después, y ningún participante puede apostar en la misma ronda tras conocer el resultado.

Seed del Sorteo

seed = keccak256(abi.encodePacked(blockhash(targetBlock), salt, block.prevrandao, roundId))
Inputs reales usados por el contrato: blockhash(targetBlock), salt, block.prevrandao y currentRoundId.

Generación de Cada Número

num = (keccak256(seed, nonce) mod 30) + 1
Si el número ya fue sorteado en esa ronda, se descarta y el nonce se incrementa. El proceso se repite hasta obtener 6 números únicos.

Este procedimiento impide números repetidos en el resultado final y mantiene el sorteo dentro del rango válido 1..30.

Importante (transparencia) El resultado de una ronda finalizada es determinista y reproducible con datos on-chain. Cualquier intento de intervención operativa (ej.: reinicio de la ronda) deja un rastro público y auditable en la blockchain.

Apuesta Múltiple (6 a 10 números) y Cuotas Combinatorias

En Block-Sena, un boleto puede contener de 6 hasta 10 números. Ese boleto representa varias combinaciones equivalentes de 6 números. El cálculo de premios se hace por cuotas ganadoras (combinaciones), y no solo por “personas”.

Total de Combinaciones de un Boleto con k números

combos(k) = C(k, 6)
Ejemplo: si k = 10, entonces C(10,6) = 210 combinaciones equivalentes.

Cuotas ganadoras por categoría (4/5/6)

Considere un boleto con k números y x aciertos dentro de las 6 decenas sorteadas.

q6 = C(x, 6)
q5 = C(x, 5) * C(k - x, 1)
q4 = C(x, 4) * C(k - x, 2)
El contrato suma estas cuotas por cartera y paga de forma agregada por token (más eficiente en gas).

Ejemplo (boleto de 10 números con los 6 sorteados dentro de esos 10)

q6 = 1, q5 = 24, q4 = 90. El mismo boleto puede ganar simultáneamente en las 3 categorías.

Payout Multi-Token (Liquidación por Token)

Cada token del pool (ej.: nativo, USDC, USDT) se liquida de forma independiente. El contrato calcula ganadores y premios por ronda y por token, manteniendo un historial exacto en RoundTokenStats.

División por Categoría

Categoría 6 aciertos = 80% del pool del token
Categoría 5 aciertos = 15% del pool del token
Categoría 4 aciertos = 5% del pool del token

Premio por cuota ganadora

prizePerWinnerX = floor(prizeBucketX / quotasX)
La división usa aritmética entera on-chain. Los restos de redondeo (dust) permanecen como carry-over para la próxima ronda.

Si no hay ganadores

El valor de esa categoría no se pierde: compone el acumulado (carry-over) del token para la próxima ronda.

Si hay múltiples ganadores

El premio se divide por cuotas ganadoras. Una misma cartera puede recibir varias cuotas agregadas en un único pago por token.

Cómo Auditar una Ronda (Paso a Paso)

  1. Localiza la tx de blockSenaPauseDraw y anota el commitHash y el targetBlock.
  2. Después del objetivo, localiza la tx de blockSenaDrawPay y lee el salt enviado.
  3. Recalcula keccak256(salt) y confirma que coincide con el commitHash.
  4. Lee blockhash(targetBlock) en el explorer y el prevrandao del bloque de la tx de draw.
  5. Reproduce el seed y la rutina de generación de los 6 números (descartando duplicados) y compáralo con DrawExecuted.
  6. Si hubo una falla operativa rara, revisa el evento DrawReset (roundId, targetBlock y bloque del reset) para validar el timeout y la trazabilidad del reset.
  7. Verifica los eventos RoundTokenSettled para pools, pagos por categoría y carry-over por token.
  8. Confirma transfers nativos/ERC20 para validar los pagos a los ganadores.

Garantías, Transparencia y Límites Operativos

Block-Sena fue diseñado para que el resultado de una ronda sea auditable, reproducible y transparente. El commit-reveal combinado con bloque futuro reduce significativamente la posibilidad de manipulación manual del resultado.

En términos técnicos, el modelo hace que el resultado sea impredecible en el momento del lock, porque la ronda se cierra antes de que exista el blockhash(targetBlock) usado en el seed. Así, la fórmula sigue siendo pública y auditable sin permitir apuestas posteriores basadas en el resultado.

Qué está garantizado on-chain

Reglas del sorteo, cálculo de cuotas, payout por categoría (80/15/5), historial por token, eventos y trazabilidad de cada etapa.

Operación de emergencia

Existe una función de reset del draw para escenarios técnicos raros (ej.: expiración del blockhash por falla prolongada de infra/red). Solo puede ejecutarse con la ronda bloqueada y tras timeout on-chain. Todo uso es público, queda registrado on-chain y puede auditarse en el explorer.

El reset no mueve fondos, no borra boletos y no altera el pool. Solo desbloquea el ciclo de la ronda para permitir un nuevo intento de sorteo de la misma ronda en caso de falla rara.

Los valores del premio permanecen en el contrato hasta el sorteo y los pagos automáticos a los ganadores. Si no hay ganadores en una categoría, el valor permanece contabilizado como acumulado (carry-over) para la próxima ronda. El owner no tiene función para retirar fondos contabilizados del pool de premios.

La función rescueAll existe solo para rescatar excedentes no contabilizados (ej.: envío directo por error), calculando saldo real - pool contabilizado. No alcanza los fondos del premio ya contabilizados.

En otras palabras: Block-Sena no pide confianza ciega. Entrega un flujo que puede ser acompañado, reproducido matemáticamente y verificado on-chain por cualquier persona, en cualquier momento.