Travaux en cours

Travaux en cours : nous construisons ce site en vue du MainNet. Les détails vont évoluer.

Ce site est encore en construction

ZooBC se construit au grand jour. Ce que vous voyez ici est actuel et honnête, mais pas terminé : lisez-le comme l'état des choses aujourd'hui, et non comme une version définitive.

D'ici le MainNet, des choses vont changer : formulations, structure, images et chiffres. Certaines pages sont provisoires.

L'écosystème ZooBC élargi arrive par étapes. Le portefeuille, l'explorateur et les canaux communautaires sont mis en ligne à mesure que le MainNet approche, et ce site grandit avec eux.

Si quelque chose vous semble erroné, cassé ou trompeur, dites-le-nous. Un retour aujourd'hui compte plus pour nous qu'un lancement soigné plus tard.

ZOOBC / MANUEL

Applications sur la chaîne ZooBC

Manuel des applications

Types de transaction 24, 25, 26, 27, 28 et 39. Corps, registre des applications, paiements, et comment vérifier vous-même un résultat. Transcrit à partir des sources du nœud.

Une application sur ZooBC est un jeu ou un pari dont les règles s'exécutent dans le consensus. Chaque coup est une transaction, le

plateau réside dans l'état de la chaîne et le paiement est effectué par le protocole. Aucun serveur auquel se fier, aucun

opérateur qui puisse refuser de payer, aucun résultat à croire sur parole : tout est

rejouable à partir de l'historique de la chaîne, par n'importe qui, pour toujours.

Ce manuel est la référence de l'intégrateur : les six types de transaction, les corps exacts, le registre

des applications avec l'encodage des coups de chacune, la circulation de l'argent, et la façon dont l'aléa est dérivé et vérifié.

Tout ce qui figure ici est transcrit des sources du nœud, include/zoobc/common/types.h,

src/transaction/app_rules.cpp, et src/transaction/transaction_executor.cpp, et non des notes

de conception, antérieures à l'implémentation et qui en diffèrent par endroits.

1. Les trois catégories

CatégoriePlacesContrepartieAléaTypes
Solo contre la banque1la réserve des applications du protocoleoui, graine du bloc16–21
Face-à-face2un autre joueurseulement si l'application l'utilise1–8
Groupe3–4d'autres joueursdés tirés de la graine du bloc32–35

Les trois partagent un même moteur : les mêmes types de transaction, le même séquestre des mises, le même délai

par coup, et la même règle : un coup égale une transaction.

seats détermine la catégorie et est vérifié par rapport au type d'application. Une application solo doit avoir seats == 1

et un type dans la plage 16–31 ; une application multijoueur doit avoir seats 2–4 et un type hors de cette plage. Une erreur

sur ce point est rejetée à l'admission dans la mempool, pas à l'exécution.

2. Types de transaction

Six types. Tous les corps sont en little-endian ; tous les montants sont en unités atomiques (divisez par 1e8 pour obtenir des ZBC).

TypeNomCorps
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)]*

Les deux champs finaux facultatifs de CreateApp

opponent et channel sont tous deux facultatifs, et leur présence se déduit du nombre d'octets

restant après params:

Octets restantsSignification
0application ouverte, jeu sur la chaîne
1channel seulement
36opponent seulement
37opponent puis channel

Un opponent transforme l'application en défi direct : seule cette adresse peut la rejoindre. Omettez-le et n'importe qui

peut prendre la place. channel = 1 marque une application à canal d'état et n'est valide que lorsque seats == 2.

Les applications multijoueurs stockent les joueurs dans des emplacements fixes de 36 octets : chaque adresse occupant une place doit donc être canonique sur 36 octets (une adresse ZBC ou une clé publique brute de 32 octets). Les comptes qui ne font pas 36 octets sont rejetés à la création. Les applications solo n'ont pas cette restriction, puisqu'il n'y a qu'un seul joueur.

3. Le registre des applications

app_type est un seul octet. Seules ces valeurs sont connues ; toute autre valeur est rejetée.

Face-à-face (2 places)

TypeApplicationPlateau / étatOctets du coup
1Morpion9 cases[cell 0..8]
2Échecs64 cases[from, to], légalité complète, échec, échec et mat, pat, promotion automatique en dame
3Connect-442 cases (7×6)[column 0..6], gravité, 4 alignés
4Dames64 cases + verrou de prise multiple[from, to], prise obligatoire, prises multiples enchaînées, dames
5Reversi64 cases[cell 0..63], au moins un pion doit être retourné ; un camp sans coup possible passe ; le plus de pions l'emporte
6Gomoku225 cases (15×15)[x, y], 5 alignés
7Bataille navale6400 d'engagement + 200 de révélationengagement-révélation, voir plus bas
8Jeu des petits carrés24 segments + 9 carrés[edge 0..23], compléter un carré donne un coup supplémentaire

Solo contre la banque

TypeApplicationparamsGains
16Deux dés[bet_type 0..3, total]moins de 7 / plus de 7 2.28×, 7 chanceux 5.7×, total exact 3420/combinaisons %
17Pile ou face[choice 0/1]1.98×
18Roulette[bet_type, value]numéro plein 36×, couleur 2×
19Machine à sousaucunetrois identiques 15×, triple 7 50×, toute paire 1.8×
20Loterie[pick 0..99]90×
21Crash[target ×100, 2 bytes LE]cible ×, de 1.01× à 10.00×

Les types de pari aux dés sont 0 moins de 7, 1 7 chanceux, 2 plus de 7, 3 total exact. Pour un total exact, le

multiplicateur est 3420 / ways en centièmes, où ways = 6 - |7 - total| : 7 paie donc 5.70× et 2 ou

12 paient 34.20×. La roulette a 37 cases ; le 0 est vert et perd sur les deux couleurs.

Groupe (3–4 places)

TypeApplicationÉtatOctets du coup
32Petits chevauxseats×4 positions des pions + dé en attentedeux phases : lancer, puis choisir un pion
33Pigseats scores + total du tour[0 roll, 1 hold]
34Course (serpents et échelles)seats positions[0], lancer
35Monopoly simplifiéargent, positions, propriétés, faillite, phasedeux phases : lancer, puis acheter éventuellement

4. L'argent

Mises. CreateApp place la mise du créateur sous séquestre. Chaque JoinApp place une mise égale sous séquestre. Le pot

en est la somme. Un stake_token_id de 0 signifie ZBC ; toute autre valeur mise ce jeton coloré, et

toute l'application (pot, commission, gains) est réglée dans ce jeton.

Commission. En cas d'issue décisive, 1 % du pot va à la réserve des applications et le gagnant reçoit le

reste. En cas d'égalité ou d'application annulée, toutes les mises sont remboursées et aucune commission n'est prélevée.

La réserve des applications est un compte du protocole avec un solde par jeton. Elle est alimentée par cette commission de 1 %, plus

l'avantage de la banque sur les applications solo. Elle est la contrepartie de chaque pari solo : c'est donc son solde qui rend

le jeu solo possible, et vous pouvez le consulter à tout moment :


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

Plafond de bankroll. Le gain maximal possible d'un seul pari solo ne peut pas dépasser 5 % de la réserve pour

ce jeton. La vérification porte sur le pire cas du pari pour la banque, pas sur sa moyenne : aucun

gain isolé ne peut donc vider la réserve. Un pari qui dépasserait ce plafond est rejeté ; si un gros pari est refusé,

c'est généralement pour cette raison.

5. L'aléa, et comment le vérifier

Les applications solo se résolvent à partir de la graine du bloc, qui sur ZooBC est

block_seed = blocksmith_signature(SHA3(previous_block.block_seed)), déterministe, publiquement

vérifiable avec la clé publique du forgeur de bloc, et impossible à connaître avant que le bloc n'existe.

Un pari placé dans le bloc à la hauteur H se résout à H + 2:


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

Le joueur s'engage deux blocs avant que la graine qui servira à la résolution n'existe : il ne peut donc

ni la prédire ni la choisir. N'importe qui peut ensuite recalculer r à partir de la graine du bloc et de l'identifiant de l'application, et

donc recalculer le résultat. Rien dans le résultat ne dépend de données que la chaîne ne détient pas.

Crash dérive son point de crash du même r:


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

Le pari gagne stake × target / 100 lorsque C >= target. Le 99 au numérateur est l'avantage

de la banque, 1 %. Le plafond est fixé à 10.00× : le plus gros gain possible au crash est donc dix fois la mise.

Multi-parties. Toute application solo peut être jouée jusqu'à 100 fois avec un seul pari. Ajoutez un octet de compteur

final N à params, après les octets de choix de l'application. La mise est répartie équitablement entre les N parties

et les gains sont additionnés : une transaction et une révélation produisent ainsi N résultats indépendants sans

attendre un bloc pour chacun. Omettre N signifie une seule partie, et une seule partie est identique, au bit près, à ce qu'elle

aurait été avant l'existence du multi-parties. N doit être compris entre 1 et 100 et la mise doit être divisible de sorte que chaque partie

mise au moins une unité.

6. Tours, délais et fins de partie

Une application devient active lorsque sa dernière place est occupée. Dès lors, chaque coup accepté fixe


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

Un coup n'est accepté que du joueur dont c'est le tour, que tant que l'application est active, et que

avant l'expiration du délai. Une application peut se terminer de quatre façons :

  • Une victoire ou une égalité selon les règles. Réglée immédiatement : le gagnant est payé, ou toutes les mises sont remboursées en cas d'égalité.
  • ResignApp. Vous abandonnez ; votre part du pot va à l'adversaire.
  • ClaimAppTimeout. Votre adversaire a laissé passer le délai de 240 blocs. Vous réclamez, et vous remportez le pot.

C'est à vous de réclamer : rien ne se passe automatiquement du seul fait que le délai a expiré.

  • Personne ne rejoint. Une application ouverte que personne ne rejoint est annulée et la mise remboursée.

7. Canaux d'état et SettleApp

Pour une application en face-à-face, jouer chaque coup sur la chaîne coûte une transaction et un bloc par coup. Un

canal d'état remplace cela par un règlement unique : créez avec channel = 1 et seats = 2, jouez

hors chaîne, chaque camp signant chaque coup, et soumettez la partie entière en une fois sous forme de SettleApp (type 39).

La chaîne rejoue les coups signés, dans l'ordre, depuis l'état initial de l'application, avec le même moteur

de règles, vérifie chaque signature et chaque tour, puis paie le gagnant. Un règlement peut être supplanté par

un autre portant un final_seq plus élevé pendant 240 blocs après son inclusion ; une fois cette fenêtre de contestation

écoulée, il est définitif.

Trois limites s'appliquent au corps, et un intégrateur doit respecter les trois :

  • final_seq doit être égal à move_count.
  • move_count ne peut pas dépasser 1024 entrées.
  • move_count ne peut pas dépasser ce que le reste du corps peut physiquement contenir. Chaque entrée fait au moins 67

octets, seat(1) + move_len(2) + signature(64) : un nombre supérieur à remaining / 67 est donc

rejeté avant qu'un seul octet ne soit alloué.

1024 entrées est une limite par demi-coup, et non par paire de coups : une entrée correspond à un demi-coup, donc un règlement de 1024 entrées couvre une partie de 512 coups. La plus longue partie d'échecs jamais enregistrée a duré 269 coups, soit 538 demi-coups : le plafond représente donc environ le double de la plus longue partie réellement jouée.

8. Bataille navale (type 7)

La bataille navale est la seule application en face-à-face qui nécessite des informations cachées : elle utilise donc l'engagement-révélation.

Le plateau fait 10×10. Pour chacune des 100 cases, le joueur choisit un sel aléatoire de 32 octets et s'engage sur

SHA3(is_ship ‖ salt); l'engagement est la concaténation de ces 100 hachages, soit 3200 octets, transmise en tant que

params à la création. seats doit valoir 2 et l'engagement doit faire exactement 3200 octets, sinon la création est

rejetée.

Le jeu alterne deux types de coups :

  • Tir [0, cell] : vous tirez sur une case du plateau adverse. Le tour passe à l'adversaire, qui doit révéler.
  • Révélation [1, cell, is_ship, salt(32)] : l'adversaire prouve ce qui s'y trouvait en ouvrant l'engagement

de cette seule case. Comme chaque case fait l'objet d'un engagement distinct, un joueur ne peut mentir sur aucune case.

Le résultat (touché ou manqué) est enregistré, puis celui qui a révélé tire à son tour.

Touchez les 17 cases de navires et vous gagnez. Un joueur qui refuse de révéler est soumis au délai comme pour tout autre coup :

ClaimAppTimeout s'applique donc.

Les frais augmentent avec la taille. Une création de 3200 octets est une grosse transaction, et le plancher des frais augmente avec la taille de la transaction. Sur une chaîne en service, des frais de 0.1 ZBC ont été rejetés comme trop bas et environ 5 ZBC ont été acceptés. Prévoyez-le pour la création ; les coups de tir et de révélation sont petits et bon marché.

9. Lire l'état d'une application

Cinq points de terminaison, servis par l'API du nœud ; notez qu'il s'agit de points de terminaison de nœud, et non d'archive :

URL de base : https://zoobc.network/api/v1. Il s'agit de la passerelle publique du TestNet ZooBC, pas du MainNet.

Point de terminaisonRenvoie
GET /api/v1/appsle salon d'attente ; filtrez avec status=, category=solo|pvp|party, limit=
GET /api/v1/apps/openles défis en face-à-face ouverts que tout le monde peut rejoindre
GET /api/v1/apps/:idl'état complet d'une application
GET /api/v1/apps/poolles soldes de la réserve des applications, par jeton
GET /api/v1/apps/statscompteurs en direct : total, ouvertes, actives, terminées, joueurs, pot en jeu

Une ligne d'application contient id, app_type, status (0 ouverte, 1 active, 2 terminée, 3 annulée), 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 et channel.

Le state_blob est le plateau dérivé actuel, conservé pour qu'un nœud n'ait pas à rejouer l'historique à chaque bloc.

C'est une commodité, pas la référence : la référence, ce sont les transactions de coups, et un client peut les rejouer

pour un audit ou une animation. La ligne en direct d'une application terminée est supprimée après un délai de grâce ; ses coups restent

dans l'historique de la chaîne pour toujours : l'application peut donc toujours être reconstituée.

10. En ligne de commande

zoobc-cli couvre les six types. Le premier paramètre est toujours la clé privée de l'expéditeur.

CommandeType
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 liste les champs de chacune d'elles. Consultez le Manuel de la CLI pour l'ensemble des

commandes et le Manuel des transactions pour la structure en octets des 50 types de transaction.

11. Constantes

ConstanteValeurCe qu'elle régit
Commission1 % du potversée à la réserve des applications, uniquement en cas d'issue décisive
Délai par coup240 blocs~1 heure ; réinitialisé à chaque coup accepté
Résolution solohauteur du pari + 2~30 secondes
Plafond de bankroll5 % de la réservegain maximal d'un seul pari solo
Plafond multi-parties100parties par pari solo
Plafond de coups par règlement1024entrées par SettleApp, ≥67 octets chacune
Fenêtre de contestation du règlement240 blocs~1 heure pendant laquelle un final_seq plus élevé l'emporte
Places1, ou 2–41 pour le solo ; 2–4 pour le multijoueur
Engagement de la bataille navale3200 octetsexactement, sinon la création est rejetée