Wiki ZooBC
Applis et développement sur ZooBC

Construire une appli en une seule page HTML

Une application ZooBC peut tenir dans un seul fichier HTML, sans aucun serveur derrière. La page lit la chaîne par l’API HTTP d’un nœud, construit et signe les transactions dans le navigateur avec la clé de l’utilisateur, puis envoie les octets signés. Les utilisateurs n’exécutent pas de nœud, et il n’y a aucune clé API à demander. Le portefeuille officiel en est la preuve : il est lui-même un seul fichier HTML, que chaque nœud sert à / et à /wallet (portefeuille web).

Un fichier HTML lit avec GET, signe dans le navigateur et envoie les octets signés à un nœud ou à une passerelle.
Un fichier HTML lit avec GET, signe dans le navigateur et envoie les octets signés à un nœud ou à une passerelle.

L’API du nœud

Chaque nœud sert une API HTTP/1.1 en clair, sur le port 8080 par défaut, avec plus d’une centaine de routes sous /api/v1/ : blocs, comptes, jetons, applis, le marché et bien plus (API du nœud). Chaque réponse porte ces en-têtes, et une requête OPTIONS reçoit immédiatement une réponse 200, si bien qu’une page hébergée n’importe où peut appeler n’importe quel nœud :

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Node-Public-Key, X-Timestamp, X-Signature

Le nœud lui-même n’a pas de TLS. Pour les domaines publics, la terminaison TLS se fait sur une passerelle, qui ajoute ses propres en-têtes CORS. Les navigateurs bloquent les appels en HTTP simple depuis une page chargée en HTTPS ; une page publique s’adresse donc à une passerelle.

Toutes les routes sauf huit sont des lectures sans authentification, et l’envoi d’une transaction ne demande aucune clé, quelle qu’elle soit, car la transaction porte sa propre signature. Des limites s’appliquent par adresse client : 300 requêtes d’écriture par minute, puis 429 avec un en-tête Retry-After ; les lectures sont illimitées, sauf si l’opérateur fixe une limite ; et 20 échecs d’authentification signée en 5 minutes bloquent le client pendant 300 secondes.

Signer dans le navigateur

La page signe un condensé qui lie la transaction à une seule chaîne :

digest = SHA3-256("ZBC-TX" ‖ genesis hash ‖ unsigned transaction bytes)

Un compte natif le signe avec Ed25519. La page lit genesis_hash et signing_version (2) depuis GET /api/v1/node/info, et comme le hachage de genèse figure dans le condensé, une signature faite pour une chaîne ne vaut rien sur une autre (signatures liées à la chaîne). Aucun SDK n’est nécessaire : Ed25519, SHA3-256 et le hachage de genèse suffisent, et la disposition des octets est décrite dans l’enveloppe de la transaction. Les outils publics proposent aussi des bibliothèques prêtes à l’emploi, dont un fichier JavaScript sans dépendance à charger par une simple balise script (outils en ligne de commande). La clé ne quitte jamais la page.

Envoi et suivi

POST /api/v1/transactions               → 202, with "transaction_hash"
GET  /api/v1/transactions/<hash>/status → staging, mempool, confirmed (with block_height),
                                          rejected (with the reason and stage), or 404 not_found

Une réponse 202 signifie «  acceptée  », pas «  terminée  ». Le nœud a analysé la transaction, effectué ses premières vérifications et l’a prise dans sa zone de préparation (staging) ; un producteur de blocs doit encore l’inclure. Tant que le statut n’indique pas confirmed, aucun solde n’a changé. À raison d’un bloc toutes les 15 secondes, interroger le nœud toutes les deux secondes environ est largement suffisant.

Si une réponse est perdue, renvoyez les octets signés à l’identique. Le hachage de la transaction est le SHA3-256 des octets non signés et de la signature : un nœud qui détient déjà la transaction répond donc 202 avec "duplicate": true, en ajoutant "state": "confirmed" une fois qu’elle est dans un bloc, et la transaction ne s’exécute qu’une seule fois. Signer de nouveau avec un nouvel horodatage crée une transaction différente, et c’est ainsi que surviennent les doubles paiements.

Par défaut, un bloc peut inclure une transaction jusqu’à 3 600 secondes après son horodatage ; une transaction de version 3 indique sa propre fenêtre, jusqu’à 86 400 secondes ; et un horodatage situé plus de 3 600 secondes dans le futur est refusé (expiration et horodatages). Avant de livrer des biens contre un paiement, attendez la confirmation, puis l’indicateur finalized, qu’une transaction obtient une fois qu’un bloc dorsal cosigné couvre son bloc (finalité et retour arrière). L’API indique aussi finalized_height, le block_seed_proof de chaque bloc, l’expiry d’une transaction et les blocs dorsaux à /spine/blocks/:height.

Après 202 : staging, mempool, confirmed, finalized ; une réponse perdue est renvoyée avec les mêmes octets.
Après 202 : staging, mempool, confirmed, finalized ; une réponse perdue est renvoyée avec les mêmes octets.

Routes des applis

C’est le nœud lui-même, et non le service d’archivage, qui sert les routes des applis : /apps (filtres status, limit et category, qui vaut solo, pvp ou party), /apps/open, /apps/pool, /apps/stats, /apps/:id (avec l’éventuel règlement) et /accounts/:a/apps.

Liste de contrôle

  • Lisez par une passerelle en HTTPS ; écrivez en envoyant des octets signés, la clé restant dans la page.
  • Suivez par hachage, et réessayez en renvoyant les mêmes octets, jamais en signant de nouveau.
  • Respectez le code 429 et Retry-After.
  • Affichez le texte de la chaîne avec textContent (données de la chaîne non fiables).
  • Considérez «  confirmed  » comme reçu et «  finalized  » comme réglé.

Voir aussi