Trabajo en curso

Trabajo en curso: estamos construyendo este sitio antes de la MainNet. Los detalles cambiarán.

Este sitio aún está en construcción

ZooBC se construye a la vista de todos. Lo que ves aquí es actual y honesto, pero no está terminado: léelo como el punto en el que estamos hoy, no como una declaración definitiva.

De aquí a la MainNet habrá cambios: redacción, estructura, imágenes y cifras. Algunas páginas son provisionales.

El ecosistema ZooBC en su conjunto llega por etapas. La billetera, el explorador y los canales de la comunidad se irán activando a medida que se acerque la MainNet, y este sitio crece con ellos.

Si algo te parece incorrecto, roto o engañoso, dínoslo. Tus comentarios ahora valen más para nosotros que un lanzamiento pulido más adelante.

ZOOBC / MANUAL

Aplicaciones en la cadena de ZooBC

Manual de aplicaciones

Tipos de transacción 24, 25, 26, 27, 28 y 39. Cuerpos, el registro de aplicaciones, los pagos y cómo verificar un resultado por tu cuenta. Transcrito de las fuentes del nodo.

Una app en ZooBC es un juego o una apuesta cuyas reglas se ejecutan en el consenso. Cada jugada es una transacción, el

tablero vive en el estado de la cadena y el pago lo realiza el protocolo. No hay ningún servidor en el que confiar, ningún

operador que pueda negarse a pagar y ningún resultado que haya que aceptar por fe: cualquiera puede reproducirlo

todo a partir del historial de la cadena, para siempre.

Este manual es la referencia para integradores: los seis tipos de transacción, los cuerpos exactos, el registro

de apps con la codificación de jugadas de cada una, cómo se mueve el dinero y cómo se obtiene y se verifica la aleatoriedad.

Todo lo que aparece aquí está transcrito de las fuentes del nodo, include/zoobc/common/types.h,

src/transaction/app_rules.cpp, y src/transaction/transaction_executor.cpp, no de las notas

de diseño, que son anteriores a la implementación y difieren de ella en algunos puntos.

1. Las tres categorías

CategoríaPlazasContraparteAleatoriedadTipos
Individual contra la casa1el fondo de apps del protocolosí, semilla del bloque16–21
Uno contra uno2otro jugadorsolo si la app la usa1–8
Grupo3–4otros jugadoresdados a partir de la semilla del bloque32–35

Las tres comparten un mismo motor: los mismos tipos de transacción, el mismo depósito en garantía del stake, el mismo plazo

por jugada y la misma regla de que una jugada es una transacción.

seats determina la categoría y se comprueba con el tipo de app. Una app individual debe tener seats == 1

y un tipo dentro de 16–31; una app multijugador debe tener seats de 2 a 4 y un tipo fuera de ese rango. Si esto

es incorrecto, se rechaza al entrar en la mempool, no en la ejecución.

2. Tipos de transacción

Seis tipos. Todos los cuerpos son little-endian; todas las cantidades son atómicas (divide entre 1e8 para obtener ZBC).

TipoNombreCuerpo
24CreateAppapp_type(1) · stake_token_id(8) · stake_amount(8) · seats(1) · params_len(2) · params · [opponent(36)] · [channel(1)]
25JoinAppapp_id(8)
26AppMoveapp_id(8) · move_len(2) · move_bytes
27ResignAppapp_id(8)
28ClaimAppTimeoutapp_id(8)
39SettleAppapp_id(8) · final_seq(4) · move_count(4) · [seat(1) · move_len(2) · move · signature(64)]*

Los dos campos finales opcionales de CreateApp

opponent y channel son ambos opcionales, y cuáles están presentes se deduce de cuántos bytes

quedan después de params:

Bytes restantesSignificado
0app abierta, juego en la cadena
1channel solamente
36opponent solamente
37opponent y después channel

Un opponent convierte la app en un desafío directo: solo esa dirección puede unirse. Si lo omites, cualquiera

puede ocupar la plaza. channel = 1 marca una app de canal de estado y solo es válido cuando seats == 2.

Las apps multijugador almacenan a los jugadores en ranuras fijas de 36 bytes, por lo que toda dirección que ocupe una plaza debe ser canónica de 36 bytes (una dirección ZBC o una clave pública de 32 bytes sin prefijo). Las cuentas que no son de 36 bytes se rechazan al crear la app. Las apps individuales no tienen esa restricción: solo hay un jugador.

3. El registro de apps

app_type es un único byte. Solo se conocen estos valores; cualquier otro se rechaza.

Uno contra uno (2 plazas)

TipoAppTablero / estadoBytes de la jugada
1Tres en raya9 casillas[cell 0..8]
2Ajedrez64 casillas[from, to], legalidad completa, jaque, jaque mate, ahogado, promoción automática a dama
3Connect-442 casillas (7×6)[column 0..6], gravedad, 4 en línea
4Damas64 casillas + bloqueo de salto múltiple[from, to], captura obligatoria, continuación de salto múltiple, damas coronadas
5Reversi64 casillas[cell 0..63], hay que voltear al menos una; el bando sin jugada pasa; gana quien tenga más fichas
6Gomoku225 casillas (15×15)[x, y], 5 en línea
7Batalla naval6400 de compromiso + 200 de revelacióncompromiso-revelación, ver abajo
8Puntos y cajas24 lados + 9 cajas[edge 0..23], quien completa una caja vuelve a jugar

Individual contra la casa

TipoAppparamsPaga
16Dos dados[bet_type 0..3, total]menos de 7 / más de 7 2.28×, 7 de la suerte 5.7×, total exacto 3420/ways %
17Cara o cruz[choice 0/1]1.98×
18Ruleta[bet_type, value]número único 36×, color 2×
19Tragamonedasningunatres iguales 15×, triple 7 50×, cualquier pareja 1.8×
20Lotería[pick 0..99]90×
21Crash[target ×100, 2 bytes LE]objetivo ×, de 1.01× a 10.00×

Los tipos de apuesta de los dados son 0 menos de 7, 1 7 de la suerte, 2 más de 7, 3 total exacto. Para un total exacto, el

multiplicador es 3420 / ways en centésimas, donde ways = 6 - |7 - total|, así que el 7 paga 5.70× y el 2 o el

12 pagan 34.20×. La ruleta tiene 37 casillas; el 0 es verde y pierde con ambos colores.

Grupo (3–4 plazas)

TipoAppEstadoBytes de la jugada
32Ludoseats×4 posiciones de las fichas + dado pendienteen dos fases: tirar y luego elegir una ficha
33Pigseats puntuaciones + total del turno[0 roll, 1 hold]
34Carrera (serpientes y escaleras)seats posiciones[0], tirar
35Monopoly simplificadodinero, posiciones, propiedades, bancarrota, faseen dos fases: tirar y luego, opcionalmente, comprar

4. Dinero

Stakes. CreateApp retiene en garantía el stake del creador. Cada JoinApp retiene un stake igual. El bote

es la suma. Un stake_token_id de 0 significa ZBC; cualquier otro valor apuesta ese token coloreado, y toda

la app (bote, comisión de la casa, pago) se liquida en ese token.

Comisión de la casa. En un final decisivo, el 1% del bote va al fondo de apps y el ganador recibe el

resto. En un empate o una app cancelada, se reembolsan todos los stakes y no se cobra comisión.

El fondo de apps es una cuenta del protocolo con un saldo por token. Crece con esa comisión del 1% más

la ventaja de la casa en las apps individuales. Es la contraparte de toda apuesta individual, así que su saldo es lo que hace

posible el juego individual; puedes consultarlo en cualquier momento:


GET https://zoobc.network/api/v1/apps/pool

Límite de banca. El pago máximo posible de una sola apuesta individual no puede superar el 5% del fondo para

ese token. Se comprueba frente al peor caso de la propia apuesta para la casa, no frente a su media, así que ningún

acierto aislado puede vaciar el fondo. Una apuesta que lo superaría se rechaza; si se rechaza una apuesta grande,

este suele ser el motivo.

5. Aleatoriedad, y cómo verificarla

Las apps individuales se resuelven a partir de la semilla del bloque, que en ZooBC es

block_seed = blocksmith_signature(SHA3(previous_block.block_seed)), determinista, verificable

públicamente con la clave pública del blocksmith e imposible de conocer antes de que exista el bloque.

Una apuesta realizada en el bloque de altura H se resuelve en H + 2:


r = SHA3-256( block_seed[H+2] ‖ app_id )   →   the low 8 bytes, little-endian, as a uint64

El jugador se compromete dos bloques antes de que exista la semilla con la que se resuelve, así que no puede

predecirla ni elegirla. Cualquiera puede recalcular r después a partir de la semilla del bloque y el id de la app y,

por tanto, recalcular el resultado. Nada del resultado depende de datos que la cadena no contenga.

Crash obtiene su punto de caída del mismo r:


u = r mod 1e6
C = 99_000_000 / (1_000_000 - u)      clamped to [100, 1000], i.e. 1.00× .. 10.00×

La apuesta gana stake × target / 100 cuando C >= target. El 99 del numerador es la ventaja

de la casa, el 1%. El límite de 10.00× es el techo, así que la mayor ganancia posible en Crash es diez veces la apuesta.

Multijugada. Cualquier app individual puede jugarse hasta 100 veces con una sola apuesta. Añade un byte

final de recuento N a params, después de los bytes de elección de esa app. El stake se reparte a partes iguales entre las N jugadas

y los pagos se suman, así que una transacción y una revelación producen N resultados independientes sin

esperar un bloque para cada uno. Omitir N significa una sola jugada, y una sola jugada es idéntica bit a bit a lo que

habría sido antes de que existiera la multijugada. N debe estar entre 1 y 100, y el stake debe poder dividirse de modo que cada jugada

apueste al menos una unidad.

6. Turnos, plazos y finales

Una app se activa cuando se ocupa su última plaza. A partir de entonces, cada jugada aceptada establece


deadline_height = current_height + 240        (~1 hour at 15 s per block)

Una jugada solo se acepta del jugador al que le toca, solo mientras la app está activa y solo

antes de que venza el plazo. Una app puede terminar de cuatro maneras:

  • Una victoria o un empate según las reglas. Se liquida de inmediato: se paga al ganador o, en caso de empate, se reembolsan todos los stakes.
  • ResignApp. Abandonas; tu parte del bote va al oponente.
  • ClaimAppTimeout. Tu oponente dejó pasar el plazo de 240 bloques. Lo reclamas y ganas el bote.

Te corresponde a ti reclamarlo: no ocurre nada automáticamente solo porque el plazo haya vencido.

  • Nadie se une. Una app abierta que nadie acepta se cancela y se reembolsa el stake.

7. Canales de estado y SettleApp

En una app uno contra uno, hacer cada jugada en la cadena cuesta una transacción y un bloque por jugada. Un

canal de estado cambia eso por una única liquidación: crea la app con channel = 1 y seats = 2, juega

fuera de la cadena con cada parte firmando cada jugada, y envía la partida completa una sola vez como SettleApp (tipo 39).

La cadena reproduce las jugadas firmadas y ordenadas desde el estado inicial de la app con el mismo motor

de reglas, verifica cada firma y cada turno, y paga al ganador. Una liquidación puede ser reemplazada por

otra con un final_seq mayor durante 240 bloques después de registrarse; cuando ese periodo de impugnación

termina, queda liquidada.

Se aplican tres límites al cuerpo, y un integrador tiene que respetar los tres:

  • final_seq debe ser igual a move_count.
  • move_count no puede superar 1024 entradas.
  • move_count no puede superar lo que el resto del cuerpo puede contener físicamente. Cada entrada ocupa al menos 67

bytes, seat(1) + move_len(2) + signature(64), así que un recuento mayor que remaining / 67 se

rechaza antes de reservar un solo byte.

1024 entradas es un límite por medio movimiento, no por par de jugadas: una entrada es un medio movimiento (ply), así que una liquidación de 1024 entradas cubre una partida de 512 jugadas. La partida de ajedrez más larga registrada duró 269 jugadas, es decir, 538 medios movimientos, así que el límite es aproximadamente el doble de la partida más larga que se haya jugado nunca.

8. Batalla naval (tipo 7)

Batalla naval es la única app uno contra uno que necesita información oculta, por lo que usa compromiso-revelación.

El tablero es de 10×10. Para cada una de las 100 casillas, el jugador elige una sal aleatoria de 32 bytes y se compromete con

SHA3(is_ship ‖ salt); el compromiso son esos 100 hashes concatenados, 3200 bytes, que se pasan como

params al crear la app. seats debe ser 2 y el compromiso debe tener exactamente 3200 bytes; de lo contrario, la creación se

rechaza.

El juego alterna dos tipos de jugada:

  • Disparar [0, cell]: disparas a una casilla del tablero del oponente. El turno pasa a él para que revele.
  • Revelar [1, cell, is_ship, salt(32)]: el oponente demuestra qué había abriendo el compromiso

de esa casilla. Como cada casilla se compromete por separado, un jugador no puede mentir sobre ninguna casilla.

Se registra el acierto o el fallo, y quien revela dispara a continuación.

Si aciertas las 17 casillas de barco, ganas. Un jugador que no revela está sujeto al plazo como en cualquier otra jugada,

así que se aplica ClaimAppTimeout.

Las comisiones crecen con el tamaño. Una creación de 3200 bytes es una transacción grande, y la comisión mínima crece con el tamaño de la transacción. En una cadena en vivo, una comisión de 0.1 ZBC se rechazó por insuficiente y se aceptó una de unos 5 ZBC. Tenlo en cuenta en el presupuesto al crear la app; las jugadas de disparo y revelación son pequeñas y baratas.

9. Lectura del estado de una app

Cinco endpoints, servidos por la API del nodo; ten en cuenta que son endpoints de nodo, no de archivo:

URL base https://zoobc.network/api/v1. Es la pasarela pública de la TestNet de ZooBC, no la MainNet.

EndpointDevuelve
GET /api/v1/appsel lobby; filtra con status=, category=solo|pvp|party, limit=
GET /api/v1/apps/opendesafíos uno contra uno abiertos a los que cualquiera puede unirse
GET /api/v1/apps/:idel estado completo de una app
GET /api/v1/apps/poollos saldos del fondo de apps, por token
GET /api/v1/apps/statsrecuentos en vivo: total, abiertas, activas, terminadas, jugadores, bote en juego

Una fila de app incluye id, app_type, status (0 abierta, 1 activa, 2 terminada, 3 cancelada), seats,

creator_address, players, opponent_address, stake_token_id, stake_amount, pot,

state_blob, turn, created_height, last_move_height, deadline_height, resolve_height,

winner_address, persist_height y channel.

El state_blob es el tablero derivado actual, que se conserva para que un nodo no tenga que reproducir el historial en cada bloque.

Es una comodidad, no el registro: el registro son las transacciones de jugada, y un cliente puede reproducirlas

para auditarlas o animarlas. La fila en vivo de una app terminada se elimina tras un periodo de gracia; sus jugadas permanecen

en el historial de la cadena de forma permanente, así que la app siempre puede reconstruirse.

10. Desde la línea de comandos

zoobc-cli cubre los seis tipos. El primer parámetro es siempre la clave privada del remitente.

ComandoTipo
app-create24
app-join25
app-move26
app-resign27
app-claim28
app-settle39

# a coin-flip against the house: type 17, 1 ZBC, seats=1, choice 0
zoobc-cli app-create <privkey> 17 0 100000000 1 00 --api $API

# tic-tac-toe, open to anyone, 1 ZBC
zoobc-cli app-create <privkey> 1 0 100000000 2 --api $API

# take the middle square
zoobc-cli app-move <privkey> <app_id> 04 --api $API

zoobc-cli help app-create muestra los campos de cualquiera de ellos. Consulta el Manual de la CLI para ver el conjunto

completo de comandos y el Manual de transacciones para ver la disposición de bytes de los 50 tipos de transacción.

11. Constantes

ConstanteValorQué regula
Comisión de la casa1% del boteal fondo de apps, solo en un final decisivo
Plazo por jugada240 bloques~1 hora; se reinicia con cada jugada aceptada
Resolución individualaltura de la apuesta + 2~30 segundos
Límite de banca5% del fondopago máximo de una sola apuesta individual
Límite de multijugada100jugadas por apuesta individual
Límite de jugadas por liquidación1024entradas por SettleApp, ≥67 bytes cada una
Periodo de impugnación de la liquidación240 bloques~1 hora durante la cual gana un final_seq mayor
Plazas1, o 2–41 es individual; 2–4 es multijugador
Compromiso de Batalla naval3200 bytesexactos; si no, la creación se rechaza