Lavori in corso

Lavori in corso: stiamo costruendo questo sito in vista della MainNet. I dettagli cambieranno.

Questo sito è ancora in costruzione

ZooBC si costruisce alla luce del sole. Quello che vedi qui è attuale e onesto, ma non è finito: leggilo come il punto in cui siamo oggi, non come una versione definitiva.

Da qui alla MainNet le cose cambieranno: testi, struttura, immagini e cifre. Alcune pagine sono segnaposto.

L'ecosistema ZooBC nel suo insieme arriva per gradi. Il wallet, l'explorer e i canali della community entrano in funzione man mano che la MainNet si avvicina, e questo sito cresce insieme a loro.

Se qualcosa sembra sbagliato, non funziona o appare fuorviante, diccelo. Un feedback adesso vale per noi più di un lancio impeccabile domani.

ZOOBC / SVILUPPATORI

Sviluppatori

Tutto ciò che serve a un programma per leggere o scrivere su questa catena. Un gateway è una porta d'ingresso HTTPS: termina il TLS, applica limiti di frequenza e inoltra l'API della catena ai nodi, così un browser può comunicare con ZooBC senza dover gestire un nodo.

Su quale catena stai sviluppando?

Gli esempi seguenti vengono eseguiti sul gateway pubblico della TestNet. Indirizzi, saldi e hash delle transazioni non valgono da una rete all'altra, quindi verifica su quale rete ti trovi prima di agire.

MainNetzoobc.net
Stato della catenaRiferimento API

La MainNet apre il 14 febbraio 2027

Un solo URL di base

Tutto ciò che riguarda la catena si trova sotto /api/v1/ su questo gateway, con la stessa origine di questa pagina: nessun CORS da negoziare, nessuna chiave da ottenere, nessun piano tariffario.

zoobc.network è il gateway pubblico della TestNet di ZooBC, non della MainNet. Gli esempi qui sotto vengono eseguiti sulla rete di test.

curl https://this-gateway/api/v1/blockchain/status

Letture e scritture vanno in posti diversi, e questo conta più di quanto sembri:

GETinoltrata tramite proxy a un nodo di archivio, che conserva la cronologia completa: blocchi, transazioni e account a qualsiasi altezza.
POSTinoltrata direttamente a un nodo completo, mai a un nodo di archivio. L'invio di una transazione richiede una mempool, e un nodo di archivio è un servizio di cronologia in sola lettura: rispondeva alle POST con un 404, ecco perché un tempo la trasmissione tramite un gateway non funzionava.

Le route che userai per prime

/api/v1/blockchain/statusaltezza, altezza confermata, ultimo blocco
/api/v1/blocks/latestil blocco più recente per intero
/api/v1/accounts/<address>saldo e saldo spendibile. Accetta un indirizzo ZBC_… o i 36 byte grezzi in esadecimale
/api/v1/accounts/<address>/tokenstutti i token posseduti da quell'account
/api/v1/transactions?limit=25transazioni firmate recenti
/api/v1/movements/latestle variazioni di saldo generate dalla catena stessa: ricompense, vesting, rimborsi
/api/v1/exchange/marketsmercati aperti; …/orderbook?market=<id> per la profondità, …/offers per i pacchetti di swap
/api/v1/tokenstutti i token, con offerta e decimali
/api/v1/release/listi rilasci pubblicati e i loro hash, ciò con cui /verify effettua il confronto
/api/v1/registry/nodesil registro dei nodi; inoltre /gateways, /relays, /archivals

Alcune route esistono solo su un nodo di archivio e alcune solo su un nodo completo. Questo gateway prova prima il nodo di archivio e ritenta con l'API del nodo in caso di 404 con corpo vuoto, quindi raramente devi preoccuparti di quale sia quale; ma un 404 con un corpo è una risposta vera, non una strada sbagliata.

Inviare una transazione

Creala e firmala con zbc-cli, che riceve i parametri come JSON su stdin, così una chiave privata non finisce mai in ps né nella cronologia della shell:

echo '{"sender_privkey":"…","recipient":"ZBC_…","amount":100000000}' \
  | zbc-cli send --json-input --api https://this-gateway

1 ZBC equivale a 108 unità atomiche. Ogni importo nell'API è espresso in unità atomiche: nel formato di trasmissione non ci sono decimali da nessuna parte.

Cosa si sta costruendo su queste infrastrutture

Gli stessi tipi di transazione che chiami qui sono la base del lavoro in corso:

Agenti AItrasferimenti programmati, escrow e multisig usati come tetto di spesa che un agente autonomo non può alzare da solo. Funziona già oggi, con un esempio Python di riferimento senza dipendenze sulla TestNet pubblica.
AI decentralizzataspecialisti di modelli gestiti in modo indipendente, assemblati lato utente e regolati apertamente, così che nessuna singola azienda si frapponga tra una persona e l'intelligenza che usa. Direzione per il dopo MainNet.
ProxCellconferma ottimistica locale per i pagamenti di persona, con livelli di regolamento geografici al di sopra. Documento di lavoro.

Riferimento completo

Ogni route, i suoi parametri e la struttura della risposta:

Apri il riferimento API →  ·  vista alternativa

Entrambi gli indirizzi servono lo stesso documento.

Download

I file che un operatore ha pubblicato su questo gateway sono serviti su /dl/<name>. I nomi sono un singolo segmento di [A-Za-z0-9._-], e tutto viene inviato come allegato e mai visualizzato: questa origine serve anche il wallet, e una pagina visualizzata qui erediterebbe l'origine del wallet.

I pacchetti di rilascio si trovano sotto /releases/<version>/<arch>/, con /releases/latest che indica quello attuale. L'installer del nodo si trova su /install.

Questo gateway è in buona salute? verifica in corso…

/gateway/status riporta ciò che questo gateway sa di sé e dei nodi che può raggiungere: modalità, dominio, quanti nodi sono nella sua allowlist, quali rispondono.

/gateway/system-stats riporta i dati della macchina: CPU, memoria, disco, uptime. È da qui che provengono i valori nella pagina del gateway.

curl https://this-gateway/gateway/status
curl https://this-gateway/gateway/system-stats

Nessuno dei due richiede una chiave. Nessuno dei due espone dati della catena: per quelli usa /api/v1/… qui sopra.

Raggiungere un nodo specifico

Le route della catena rispondono da qualsiasi nodo di archivio scelto da questo gateway. Per interrogare un nodo specifico, inserisci il suo IP nel nome host, sostituendo i punti con trattini; un -p<port> facoltativo sceglie la porta dell'API:

curl https://ip-192-168-1-100.this-gateway/api/v1/node/info
curl https://ip-192-168-1-100-p8080.this-gateway/api/v1/node/info

Rispondono solo i nodi presenti nell'allowlist di questo gateway. Qualsiasi altra richiesta viene rifiutata anziché inoltrata, quindi il nome host non può essere usato per raggiungere host arbitrari tramite il gateway.

Relay

Voce, video e condivisione dello schermo viaggiano peer-to-peer; quando un firewall blocca un collegamento diretto, un relay trasporta il traffico in cambio di credito. /relay/info riporta il suo indirizzo, la sua tariffa e l'hash di genesi della catena su cui regola i pagamenti: un wallet lo confronta con quello del proprio nodo prima di pagare, perché un relay su un'altra catena non può accreditare ciò che riceve.

curl https://this-gateway/relay/info

Pannello di amministrazione

I controlli del gateway si trovano su zoobc.network/admin. Il pannello è protetto dalla chiave admin_api_key definita in /etc/zoobc/gateway.json, che l'installer genera per ogni macchina: non esiste una chiave predefinita.

L'allowlist

Un gateway inoltra le richieste solo ai nodi che gli sono stati indicati. È questo elenco a impedire che la forma di nome host ip-… trasformi il gateway in un proxy aperto.

POST /gateway/allowlist/addammette un nodo, {"ip":"…","port":8080}
POST /gateway/allowlist/removene rimuove uno
POST /gateway/allowlist/refreshrilegge il registro e ricontrolla lo stato
GET /gateway/allowlistciò che è attualmente ammesso

Tutte e tre le scritture richiedono la chiave di amministrazione. In modalità pubblica il gateway scopre i nodi anche dal registro della catena stessa; in modalità privata usa solo static_nodes.

Strumenti per i rilasci

Il calcolatore di hash dei file calcola uno SHA-256 nel browser per qualsiasi cosa tu voglia controllare a mano, e /verify confronta un pacchetto scaricato con gli hash pubblicati dalla catena.

I due documenti

Tutto ciò di cui un team di hardware wallet ha bisogno è specificato qui, e per intero nei due PDF qui sotto. Entrambi sono stati verificati sul nodo ZooBC v0.4.0 (zoobc-main al commit 7ad161a4) e su vettori di transazione accettati da un nodo v0.4.0 attivo.

La versione 2.1 sostituisce la bozza 1 in un aspetto sostanziale: le firme delle transazioni sono ora vincolate alla catena. Due modifiche rompono la compatibilità per chi ha sviluppato sulla bozza 1: il digest di firma e il corpo di ApprovalEscrow. Nulla implementa la vecchia costruzione v0.3.2, quindi sviluppa solo sul documento attuale.

Riepilogo in una pagina

Schema di firmaEd25519 (RFC 8032, puro, senza variante prehash, senza stringa di contesto). Firma da 64 byte, chiave pubblica da 32 byte.
Cosa viene firmatoEd25519.sign(sk, SHA3-256("ZBC-TX" ‖ genesis_hash(32) ‖ unsigned_bytes)). Il digest da 32 byte è il messaggio Ed25519.
Vincolo alla catenaIl digest copre l'hash del blocco genesi della catena, quindi una firma creata per una catena non può essere riutilizzata su un'altra. Il formato di trasmissione non cambia; l'hash di genesi non è un campo della transazione.
Tag di firma"ZBC-TX", sei byte ASCII 5a 42 43 2d 54 58. Nessun terminatore, nessun prefisso di lunghezza.
Funzione di hashSHA3-256 (FIPS 202). Non Keccak-256, non SHA-256.
Derivazione delle chiaviMnemonica BIP-39, poi seed (PBKDF2-HMAC-SHA512, 2048 iterazioni), poi SLIP-0010 Ed25519, percorso m/44'/883'/account'. Tre livelli hardened, nessun livello di change o di indice.
Coin type SLIP-44883 (registrato: 883 | ZBC | ZooBC).
Account (formato di trasmissione)36 byte: u32le(0) ‖ pubkey(32). La forma esadecimale è quella accettata dall'API JSON.
Indirizzo (visualizzato)ZBC_ più 56 caratteri base32 in 7 gruppi da 8. Checksum di 3 byte = SHA3-256(pubkey ‖ "ZBC")[0..3].
Byte di versione0x01, invariato. Non è stato incrementato per il vincolo alla catena e non è un discriminatore di versione.
Unità nativa1 ZBC = 100 000 000 unità atomiche (8 decimali). Importi e commissioni sono unità atomiche u64 little-endian.
Ordine dei byteTutto ciò che viene trasmesso è little-endian, tranne gli indici figli SLIP-0010 (big-endian, come da standard).
Hash della transazionetx_hash = SHA3-256(unsigned_bytes ‖ signature); tx_id = int64le(tx_hash[0..8]). L'hash di genesi entra solo nel digest di firma.
Protezione dal replayVincolo alla catena, più l'unicità dell'hash, più una finestra di inclusione di 3600 s. Non esiste un nonce.
Commissione minima0.025 ZBC di base (2 500 000 unità atomiche) moltiplicati per la scala delle commissioni di rete, più l'affitto per dimensione. Chiedilo al nodo: GET /api/v1/blockchain/estimate-fee.
Tipi di livello 11 SendZBC, 11 TransferToken, 4 ApprovalEscrow.
Individuazione della catenaGET /api/v1/node/info restituisce genesis_hash, signing_version: 2 e signing_tag: "ZBC-TX". Leggilo dall'endpoint a cui trasmetterai.

Leggi prima questi quattro

Quattro punti che influenzano un'implementazione più di tutto il resto.

1Non esiste un nonce, e non esiste un endpoint per transazioni non firmate. ZooBC è basato su account, ma qui essere basato su account non implica né l'uno né l'altro. L'host serializza da sé la transazione; la struttura qui sotto è completa e fissata dai vettori di test.
2La firma copre un prefisso con tag, "ZBC-TX" più l'hash di genesi della catena, non solo i byte della transazione. Se sbagli questo, ogni firma fallisce con un errore generico.
3L'endpoint di trasmissione non accetta il blob della transazione firmata. Accetta i byte del corpo della transazione più la busta come chiavi JSON con nome, e ricostruisce da sé la busta.
4La cronologia viene restituita dalla voce più recente, limitata da limit. Due endpoint insieme danno un quadro completo, e l'importo dipende dal tipo anziché essere un campo della busta.

Derivazione delle chiavi e indirizzi

Riferimento: zoobc-signer/extension/lib/zoobc-crypto.js, identico byte per byte al wallet di riferimento; equivalenti lato nodo in src/crypto/slip10.cpp.

seed = PBKDF2-HMAC-SHA512(NFKD(mnemonic), "mnemonic" + passphrase, 2048, 64)
I    = HMAC-SHA512("ed25519 seed", seed);  k = I[0..32], c = I[32..64]

for index in [44, 883, account]:                       # all hardened
    data = 0x00 || k || u32be(index + 0x80000000)
    I    = HMAC-SHA512(c, data);  k = I[0..32], c = I[32..64]

pubkey = Ed25519.publicKeyFromSeed(k)                  # k is the 32-byte seed

L'account 0 è m/44'/883'/0', l'account 1 è m/44'/883'/1' e così via. La derivazione delle chiavi non è influenzata dal vincolo alla catena: un unico insieme di chiavi serve ogni catena ZooBC, cambia solo la firma.

L'indirizzo visualizzato è costruito a partire dalla chiave pubblica, non dall'account da 36 byte:

buf[35] = pubkey(32) || "ZBC"
h       = SHA3-256(buf);  buf[32..35] = h[0..3]        # 3-byte checksum
s       = base32(buf)                                   # RFC 4648 A-Z2-7, no padding, 56 chars
address = "ZBC_" + s[0..8] + "_" + s[8..16] + ...       # 7 groups, canonical length 66

La decodifica accetta trattini bassi, trattini, spazi o nessun separatore, in maiuscolo o minuscolo, e il checksum deve essere ricalcolato e confrontato. Il checksum SHA3 da 3 byte qui sopra è la definizione usata dal codice e dalla catena, quindi implementa a partire da quella e dall'implementazione di riferimento nella specifica, che riproduce gli indirizzi di esempio.

Formato di trasmissione della transazione non firmata

Fonte di verità: src/util/transaction_util.cpp:144-270. Il formato di trasmissione non è cambiato nella v0.4.0. Nessuna lunghezza o offset qui sotto è influenzato dal vincolo alla catena.

#dimensionecamponote
14transaction_typeu32le, vedi il catalogo dei tipi qui sotto
21versionsempre 0x01; il nodo rifiuta qualsiasi altro valore
38timestampu64le, secondi Unix, maggiore di 0
436sender00000000 ‖ pubkey(32) per un firmatario ZBC
54 o 4+nrecipient36 byte per ZBC; u32le(type) ‖ payload per gli altri; solo 02 00 00 00 quando non c'è destinatario
68feeu64le, unità atomiche
74body_lengthu32le
8nbodystruttura specifica per tipo
94 o varescrowsenza escrow: i 4 byte 02 00 00 00. Con escrow: approver(36) ‖ commission u64le ‖ timeout u64le ‖ instr_len u32le ‖ instruction ‖ multi_party(1)
104message_lengthu32le
11mmessagetesto semplice, 256 byte o meno per le transazioni ordinarie

Il byte finale multi_party del blocco escrow è obbligatorio. Ometterlo, o scrivere il marcatore di assenza di escrow con un valore diverso, produce sul nodo soltanto «Invalid transaction signature», senza ulteriori diagnostiche.

Esempio svolto, convalidato sulla catena

SendZBC di 1 ZBC a se stessi, commissione 0.05 ZBC, timestamp 1700000000, senza escrow, senza messaggio. È il vettore 0 dei sette presenti nella specifica.

01000000                 type = 1 (SendZBC)
01                       version
00f1536500000000         timestamp 1700000000
00000000 5e8eb28d...2152 sender    (type 0 + pubkey)
00000000 5e8eb28d...2152 recipient (type 0 + pubkey)
404b4c0000000000         fee 5 000 000
08000000                 body length 8
00e1f50500000000         body: amount 100 000 000
02000000                 no-escrow marker
00000000                 message length 0

Sono 113 byte. La preimmagine di firma ne antepone altri 38:

5a42432d5458              tag "ZBC-TX"          6 bytes
090ab3c7...a61878         genesis hash         32 bytes
0100000001...00000000     the 113 bytes above 113 bytes
                                              ---------
                                              151 bytes

digest = SHA3-256(preimage) = f37e5340...4644fc, firma 9a3b044d...03bb07, tx_hash = f00a6692...ce78b0. Un nodo v0.4.0 attivo ha restituito HTTP 202 ed esattamente quell'hash.

Procedura di firma, lato dispositivo

input : account_index, genesis_hash(32), unsigned_bytes

1. k      = SLIP10(m/44'/883'/account_index')
2. pub    = Ed25519.pub(k);  self = 00000000 || pub
3. parse unsigned_bytes; every length must land exactly at the end
4. require version == 1 and sender == self
5. decode body per type; render the approval screen; wait for the user
6. digest = SHA3-256("ZBC-TX" || genesis_hash || unsigned_bytes)
7. sig    = Ed25519.sign(k, digest)          # the digest IS the message
8. return sig(64), and tx_hash = SHA3-256(unsigned_bytes || sig) for the host to cross-check

Usa SHA3 in streaming, così il passaggio 6 non richiede l'intera transazione in RAM. Fornisci i 6 byte del tag, poi i 32 byte di genesi, poi la transazione.

Il tag è in assoluto l'errore di implementazione più probabile dell'intera specifica. "ZBC-TX" è composto da sei byte ASCII, 5a 42 43 2d 54 58, senza terminatore NUL e senza prefisso di lunghezza. Un firmware che termina la stringa, o che scrive sette byte, produce una firma dall'aspetto valido che fallisce sulla catena con un errore generico e senza diagnostica. Testalo sul vettore 0 prima di ogni altra cosa.

Cosa fa e cosa non fa il vincolo alla catena

L'host fornisce i 32 byte di genesi; il dispositivo non li cerca. La firma è quindi indipendente dalla catena, e un unico percorso di codice serve TestNet, devnet, MainNet e qualsiasi catena ZooBC privata o parallela.

Cosa faImpedisce il replay tra catene. Una firma creata per la catena A non verrà verificata sulla catena B. È lo scopo per cui è stato creato, e lo assolve completamente.
Cosa non faImpedire a un host compromesso di indicare la catena sbagliata. Il dispositivo firma per qualunque hash gli venga fornito.

Una tabella nel firmware con gli hash di genesi noti serve quindi ancora, ma per un solo scopo: mostrare sullo schermo un nome di rete affidabile, ripiegando su «UNKNOWN CHAIN» più i primi 8 caratteri esadecimali quando l'hash non è nella tabella.

Non rifiutare per impostazione predefinita le catene non riconosciute. L'hash della devnet cambia a ogni riavvio, e le catene private o parallele sono normali su ZooBC, quindi un rifiuto rigido rende il dispositivo inutilizzabile per l'integrazione e per le installazioni legittime. Se un produttore vuole quel blocco, ne faccia un'impostazione esplicita dell'utente, disattivata per impostazione predefinita.

Tipi di transazione: cosa dovrebbe supportare un firmware standard

Id del tipo = group + 256 × subtype. Esistono cinquantasei tipi. Questi tre coprono il pagamento in ZBC, il pagamento con qualsiasi token e il completamento di un escrow, e i loro corpi sono piccoli e fissi.

idnomebodyrecipient
1SendZBCamount u64le (8)obbligatorio, qualsiasi tipo
11TransferTokentoken_id i64le (8) ‖ amount i64le (8) ‖ fee_in_token u8 facoltativoobbligatorio
4ApprovalEscrowapproval u32le ‖ escrowed_transaction_hash (32) = 36 bytemarcatore vuoto; il mittente è l'approvatore

token_id è un valore con segno a 64 bit derivato da un hash, quindi può essere negativo. Le monete della Genesis hanno id da 1 a 14; ZBC stesso ha id 0 e non compare mai in TransferToken. Ogni token ha il proprio decimals (da 0 a 8), che il dispositivo non può verificare.

ApprovalEscrow è cambiato nella v0.4.0. Il corpo era approval più un id da 8 byte (12 byte); ora è approval più l'hash da 32 byte della transazione in escrow (36 byte), e il parser rifiuta qualsiasi altra lunghezza. Il motivo riguarda direttamente gli hardware wallet: un firmatario che vedeva solo un id da 8 byte non poteva controllare cosa stava sbloccando, e un id da 8 byte è individuabile con una ricerca su 2^64 per un falso escrow mostrato al dispositivo. Con l'hash completo il dispositivo può verificare ciò che approva.

Cosa verifica il dispositivo

StrutturaAnalizza gli 11 campi. Ogni prefisso di lunghezza deve essere coerente e il buffer deve terminare esattamente dopo il messaggio. In qualsiasi altro caso: rifiuta.
VersioneDeve essere 1.
MittenteDeve coincidere con l'account derivato dal dispositivo stesso per il percorso richiesto. Il dispositivo non si fida mai dei byte del mittente che gli vengono forniti: li deriva di nuovo e confronta tutti i 36 byte. È questa l'intera garanzia sul «da dove proviene».
TipoDeve far parte dell'insieme supportato, oppure deve essere attiva la modalità esperto.
Lunghezza del corpo8 per SendZBC, 16 o 17 per TransferToken, 36 per ApprovalEscrow. Byte in più: rifiuta. Il nodo accetterebbe un corpo SendZBC più lungo ignorandone la coda, ed è proprio così che un payload nascosto potrebbe viaggiare insieme alla transazione.
Timeout dell'escrowDeve essere maggiore del timestamp della transazione.
Hash di genesiDeve essere esattamente di 32 byte. Il dispositivo non lo convalida rispetto a un elenco, ma una lunghezza errata indica una richiesta malformata.
MaiAccettare un digest precalcolato da firmare. Il dispositivo calcola da sé l'hash dei byte, tag e genesi inclusi.
Limiti di dimensioneConsigliati: transazione non firmata di 2 KB o meno, messaggio di 256 B, istruzione di escrow di 512 B, rifiutando qualsiasi cosa più grande con TOO_LARGE. Le transazioni di livello 1 sono sotto i 300 byte, ma un ApprovalEscrow arriva con una seconda transazione allegata per la verifica, quindi il limite deve coprire entrambi i buffer. I payload più grandi spettano ai wallet software.

Cosa approva l'utente sullo schermo

Schermata per schermata, in quest'ordine, adattato dal firmatario di riferimento convalidato sulla catena.

ReteNome preso dalla tabella del firmware per l'hash di genesi fornito; «UNKNOWN CHAIN» più i primi 8 caratteri esadecimali quando non è nella tabella.
Tipo«Send ZBC», «Send token», «Approve escrow».
Importoamount / 1e8 con il simbolo ZBC.
DestinatarioIndirizzo completo, mai abbreviato. Un indirizzo abbreviato al centro è esattamente ciò che una chiave sosia, generata come vanity address, riesce ad aggirare.
Commissioneunità atomiche / 1e8 ZBC.
Totale in uscitaimporto più commissione in un unico numero, solo per importi in ZBC. Un importo in token e una commissione in ZBC sono unità diverse e non vanno sommati.
MessaggioMostrato se stampabile, altrimenti «N bytes (binary)» più i primi 8 caratteri esadecimali del suo SHA3-256.
EscrowApprovatore per intero, commissione, timeout come data e ora, testo dell'istruzione.
Token (tipo 11)Id del token (decimale con segno) e l'importo in unità atomiche. Se l'host fornisce decimals o symbol come suggerimenti di visualizzazione, mostra l'importo scalato con l'etichetta «as reported by the app», perché il dispositivo non può verificarli.
Approvazione dell'escrow«APPROVE» o «REJECT» in caratteri grandi, poi i dettagli verificati dell'escrow, poi la commissione.

Non è necessario mostrare i timestamp. Non hanno significato per l'utente e il nodo impone la finestra.

Protocollo tra host e dispositivo

Il trasporto è il livello USB/HID del produttore, con la sua suddivisione in blocchi già esistente. Ciò che conta è il contenuto.

comandorichiestarisposta
GET_APP_VERSIONnessunoversione del firmware, insieme dei tipi supportati, dimensione massima della tx
GET_PUBLIC_KEYaccount_index, confirm_on_devicepubkey(32), address (stringa di 66 caratteri). In fase di conferma, il dispositivo mostra l'indirizzo completo perché l'utente lo confronti con quello del sito web.
SIGN_TXaccount_index, genesis_hash(32), unsigned_tx_bytessignature(64), tx_hash(32), oppure un rifiuto tipizzato: SENDER_MISMATCH, UNSUPPORTED_TYPE, MALFORMED, USER_REJECTED, TOO_LARGE, ESCROW_HASH_MISMATCH
SIGN_MESSAGEaccount_index, message_bytessignature(64), Ed25519 grezzo, vedi la regola qui sotto

genesis_hash è essenziale, non un suggerimento di visualizzazione. Nella bozza 1 il parametro di rete poteva essere un enum, perché sceglieva solo un'etichetta. Ora è dentro il digest, quindi deve contenere i 32 byte effettivi, e un valore errato produce una firma che nessuna catena accetterà.

La regola della firma grezza. Un comando di firma grezza non deve mai firmare un input di esattamente 32 byte, e dovrebbe firmare solo input che può mostrare come testo. Un input grezzo di 32 byte potrebbe essere il digest SHA3 di una transazione che l'utente non ha mai visto, trasformando il comando dei messaggi in una firma di transazioni alla cieca. Rifiutare la lunghezza 32 chiude quella falla senza modifiche alla catena.

La sequenza completa

Website (page)         Companion / host           device                    Gateway / node
   |-- connect ---------->|                            |                          |
   |                      |-- GET_PUBLIC_KEY(0) ----->|  (shows ZBC_... address) |
   |<-- address ----------|<-- pubkey, address -------|                          |
   |-- GET /api/v1/node/info  (genesis_hash, signing_version, height) ---------->|
   |-- GET /api/v1/accounts/<addr>  (balances) --------------------------------->|
   |-- GET /api/v1/accounts/<addr>/tokens, /api/v1/tokens/<id> (decimals) ------>|
   |   user fills the form                             |                          |
   |-- GET /api/v1/blockchain/estimate-fee?type=1&body_length=8 ---------------->|
   |<-- {minimum_fee, timestamp} ------------------------------------------------|
   |-- fields {type, recipient, body, fee, message} -->|                          |
   |                      |  serialize the 11 fields   |                          |
   |                      |-- SIGN_TX(0, genesis, -->|  parse, verify, display,  |
   |                      |            bytes)         |  user approves            |
   |                      |<-- signature, tx_hash ---|                          |
   |<-- signature --------|                            |                          |
   |-- POST /api/v1/transactions {JSON} ---------------------------------------->|
   |<-- 202 {status:"success", transaction_hash} --------------------------------|
   |-- GET /api/v1/transactions/<hash>/status  (staging, mempool, confirmed) --->|

L'host confronta il transaction_hash del nodo con il tx_hash del dispositivo. Devono essere identici, altrimenti l'host ha alterato i byte dopo la firma.

Leggi genesis_hash e signing_version dall'endpoint a cui trasmetterai, perché è quella la catena che giudica la firma.

Identità della catena

L'unico identificatore di rete di ZooBC è l'hash del blocco genesi. I nodi lo confrontano prima di stabilire il peering; non esiste un byte di network-id. GET /api/v1/node/info lo restituisce insieme a due campi nuovi nella v0.4.0:

"genesis_hash": "090ab3c7...", "signing_version": 2, "signing_tag": "ZBC-TX"

signing_version assente significa che il nodo è precedente alla v0.4.0 e applica il vecchio digest non vincolato. Un host che supporta solo le catene attuali può interpretarne l'assenza come «non firmare qui». Un nodo stampa l'esadecimale in minuscolo e l'API di archivio in maiuscolo, quindi decodifica senza distinguere maiuscole e minuscole e rimuovi un eventuale 0x iniziale. I byte sono gli stessi in entrambi i casi.

Da dove leggerlo. Un gateway risponde a /api/v1/node/info dal servizio di archivio anziché da un nodo. L'archivio inoltra i due campi di firma dal nodo di cui è copia, emettendoli esattamente quando il nodo li riporta, a partire dalla build 7ad161a4. Leggi signing_version da un endpoint di nodo, o da un gateway con quella build o successiva, e interpretane l'assenza come «chiedi a un nodo» anziché come una risposta.

Trasmissione

POST /api/v1/transactions. Invariato nella v0.4.0, nessun nuovo campo. transaction_body_bytes è solo il corpo: il nodo ricostruisce la busta dai campi con nome, antepone il proprio hash di genesi e il tag, e ricalcola il digest, quindi ogni campo deve essere uguale a quanto è stato firmato.

{
  "version": 1,
  "timestamp": 1700000000,
  "transaction_type": 1,
  "fee": 5000000,
  "sender_account_address":    "00000000 5e8eb28d ... 2152",
  "recipient_account_address": "00000000 5e8eb28d ... 2152",
  "transaction_body_bytes": "00e1f50500000000",
  "signature": "9a3b044d ... 03bb07",
  "message_hex": "...optional...",
  "escrow": { "approver_address": "...", "commission": 0, "timeout": 1700003600 }
}

Il successo è HTTP 202 con {"status":"success","duplicate":false,"transaction_hash":"..."}. duplicate:true significa che il nodo la possiede già, quindi non inviarla di nuovo. Considera un successo anche 200. Il fallimento è 400 con un corpo di errore, o 503 sotto carico.

Un test di integrazione utile: il pool di staging controlla prima la firma, quindi una transazione che arriva a un errore successivo come «Sender account does not exist» ha già superato la verifica della firma. Puoi convalidare il tuo percorso di firma con una chiave senza fondi.

Quando una firma viene rifiutata

Oggi una mancata corrispondenza di catena non è distinguibile da qualsiasi altro errore di firma. Un digest legacy e un digest della catena sbagliata restituiscono entrambi lo stesso identico corpo:

{"success":false,"error":"Transaction validation failed: ValidationError: Invalid transaction signature","code":400}

Quindi verifica le cause in quest'ordine, partendo dalla più probabile:

1Il tag omesso, terminato con NUL o scritto su sette byte.
2Keccak-256 usato al posto di SHA3-256.
3Un hash genesis che non corrisponde alla catena su cui si trasmette.
4Il byte multi_party finale dell'escrow omesso.
5Un corpo ApprovalEscrow ancora di 12 byte anziché 36.

Endpoint su cui sviluppare

catenagateway (HTTPS)firmanota
testnethttps://this-gatewayin migrazione a v0.4.0il riferimento per l'integrazione dopo il passaggio
devnethttps://socialconnect.networkv0.4.011 nodi, faucet su /faucet; l'hash genesis cambia a ogni rilancio
mainnetnon ancora lanciatav0.4.0 o successivaall'avvio, leggi l'hash genesis dalla catena

Instrada le letture prima al nodo di archivio, poi al gateway, poi al nodo. Trasmetti a un gateway o a un nodo, mai a un endpoint di archivio.

Leggi l'hash genesis a runtime, non scriverlo nel codice. Quello della devnet cambia a ogni rilancio. Una tabella di hash nel firmware serve solo a mostrare un nome sullo schermo, quindi una voce obsoleta degrada a «UNKNOWN CHAIN» invece di bloccare la firma. Quando l'hash della devnet cambia, ogni firma fatta in precedenza perde validità: è la funzionalità che fa il suo lavoro.