Guide · Parte 6 di 6
Certificati di inclusione
Hai pubblicato una radice Merkle sotto la label 309, insieme alla lista delle foglie off-chain che la sostiene. Quella radice è un impegno perfettamente valido, ma non è qualcosa che puoi consegnare a un collega, allegare a un contratto o depositare in un fascicolo giudiziario. Un certificato di inclusione è proprio quel documento da consegnare: un piccolo file autocontenuto che fissa una o più foglie alla radice pubblicata, incorpora ogni fratello necessario a riderivare quella radice e nomina la transazione di Cardano la cui ora di blocco fa da testimone all'intera cosa. Chiunque può verificarlo offline, rispetto a qualsiasi explorer, senza account e senza fidarsi di chi l'ha prodotto.
Se conosci OpenTimestamps, è la stessa idea —una ricevuta portatile di prova di
esistenza— con due differenze deliberate. L'autorità di marca temporale è l'ora
di blocco della blockchain di Cardano, non un server di calendario. E la prova
viene generata e verificata interamente lato client: nessun gateway, nessun
server emittente, nessuna fiducia in noi in alcun passaggio. È l'equivalente, in
chiave prova di esistenza, di un file .ots, ancorato su Cardano e verificabile
in modo autonomo.
Cosa dimostra un certificato, e cosa no
Un certificato avanza esattamente due affermazioni crittografiche, ognuna verificabile in modo indipendente da chiunque:
- Inclusione. Una data
leafoccupa la posizioneindexdi un albero Merkle SHA-256 secondo RFC 9162 di dimensionetree_sizela cui radice èroot. Lo si dimostra ricalcolando la radice a partire dalla foglia, dal suo indice, dalla dimensione dell'albero e dal percorso di fratelli incorporato. È autocontenuto: basta il file del certificato da solo. - Ancoraggio. Quella
rootcompare alla lettera nel campomerkle[].rootdel record della label 309 trasportato dalla transazionetx_hash. Lo si dimostra leggendo quella transazione su qualsiasi explorer pubblico di Cardano e confrontando i byte. Servono il file del certificato più un explorer, nient'altro.
Insieme dimostrano che il contenuto della foglia esisteva in corrispondenza o
prima dell'ora di blocco di tx_hash.
Cosa NON dimostra un certificato
L'ora è affermata dalla blockchain pubblica, non è legata crittograficamente alla prova —esattamente come con OpenTimestamps e Chainpoint, ti stai fidando dell'ora di blocco della catena, mai di chi ha prodotto il certificato. Un certificato non è una marca temporale elettronica «qualificata» eIDAS (RFC 3161 / una TSA qualificata); è una marca temporale ancorata a una blockchain: una solida prova corroborante di un'affermazione temporale, nella stessa categoria di altre marche temporali basate su blockchain, e la formulazione nel file lo dice esattamente. Non dice nulla su chi è l'autore del contenuto (quella è una firma del record opzionale; vedi Firme), né dimostra che il contenuto non fosse noto prima. Dimostra l'esistenza entro una scadenza, non la paternità e non la novità.
Perché la prova è priva di firma
Un Receipt COSE rigoroso dell'IETF è un COSE_Sign1 il cui payload è la radice
Merkle, firmato da una qualche autorità. Qui l'autorità è la blockchain, non
una chiave in nostro possesso, perciò firmare la radice con la nostra chiave
reintrodurrebbe la fiducia nel server e infrangerebbe la proprietà di
verificabilità autonoma su cui poggia l'intero standard. Il certificato emette
invece la struttura CBOR di prova di inclusione dell'IETF esattamente come
specificata, e porta l'ancoraggio alla blockchain al posto della firma. La
matematica della prova è identica byte per byte alla codifica dell'IETF; è priva
di firma in modo deliberato ed è ancorata alla blockchain.
Il certificato JSON
L'artefatto principale è label-309-inclusion-certificate-v1: un file JSON
leggibile sia dall'uomo sia dalla macchina. Un solo file copre una o molte
foglie, e ogni elemento incorpora il proprio percorso completo di fratelli, così
che il file si riverifica per sempre: senza recupero da Arweave, senza gateway,
senza che serva il pubblicatore originale.
{
"format": "label-309-inclusion-certificate-v1",
"generated_at": "2026-06-16T12:00:00.000Z", // informational only, never trusted
"anchor": {
"chain": "cardano",
"network": "mainnet",
"tx_hash": "…64hex…",
"metadata_label": 309,
"block_time": 1781611200, // POSIX seconds — explorer-asserted
"block_time_iso": "2026-06-16T12:00:00.000Z",
"block_height": 12345678, // optional; explorer-asserted
"explorer_urls": [
"https://cardanoscan.io/transaction/…",
"https://adastat.net/transactions/…"
]
},
"merkle": {
"tree_alg": "rfc9162-sha256",
"root": "…64hex…",
"tree_size": 1024, // === the on-chain leaf_count
"leaves_list_uri": "ar://<txid>" // optional source reference
},
"items": [
{
"leaf": "…64hex…", // the content hash committed as a leaf
"leaf_alg": "sha2-256", // how to hash a file to reproduce `leaf`
"index": 42,
"proof": ["…64hex…", "…64hex…"], // siblings, leaf→root; [] for a single-leaf tree
"verified": true, // proof recomputes to merkle.root at build time
"label": "contract.pdf" // optional note/filename
}
],
"claim": "Each listed hash was included in a Merkle tree whose root was published on the Cardano blockchain in the referenced transaction under metadata label 309; therefore each hash provably existed on or before the stated block time.",
"verification": {
"method": "RFC 9162 (Certificate Transparency) SHA-256 inclusion proof. For each item, recompute the Merkle root from leaf+index+tree_size+proof and compare to merkle.root; then confirm merkle.root equals the merkle[].root in the label 309 record of anchor.tx_hash on any public Cardano explorer.",
"requires_trust_in_cardanowall": false,
"time_asserted_by": "Cardano blockchain (block time), via public explorers"
}
}Qualche dettaglio che vale la pena conoscere:
root,leafe ogni voce diproof[]sono valori grezzi da 32 byte resi in hex. I produttori li emettono in minuscolo; un verificatore accetta entrambi i casi e rifiuta qualsiasi carattere non esadecimale o stringa di lunghezza dispari.- Il
"verified": truememorizzato è il risultato del produttore al momento della costruzione. Un verificatore non vi si fida mai: ricalcola la prova da sé e riporta il proprio verdetto. Una foglia non trovata nell'albero viene registrata con"verified": falsee un campo"error", mai scartata in silenzio, così che il file sia onesto riguardo ai mancati riscontri. block_timeè la marca temporale POSIX —affermata dall'explorer— del blocco che la include.block_time_isone è la resa in UTC, solo per comodità.
La prova di inclusione in CBOR (allineata a COSE / RFC 9162)
Accanto al JSON, ogni elemento può essere esportato come un compatto artefatto
.cbor la cui struttura di prova è identica byte per byte a
draft-ietf-cose-merkle-tree-proofs.
Questo significa che qualsiasi verificatore di strutture dati verificabili
RFC 9162 / COSE può leggere la matematica della prova direttamente: il nucleo di
interoperabilità è standard, non su misura. Non porta alcuna ora di blocco
assoluta né prosa giuridica (quella vive nel JSON); è soltanto il nucleo portatile
della prova, ed è ancorato alla blockchain e privo di firma per la ragione esposta
sopra.
La prova di inclusione nuda dell'IETF —bstr .cbor [tree_size, leaf_index, inclusion_path], con il valore vds (verifiable-data-structure) 1 per
RFC 9162 SHA-256— è estraibile da sola per un verificatore COSE puro; l'ancoraggio
a Cardano viene trasportato accanto ad essa come una piccola mappa al posto della
firma Sign1.
Costruire e verificare con gli strumenti
Il formato del certificato fa parte degli strumenti pubblici e indipendenti dal
gateway: l'SDK @cardanowall/sdk-ts (con gemelli identici byte per byte in Python
e Rust), la CLI cardanowall e le superfici di verifica nelle applicazioni. La
matematica di costruzione e verifica è pura e offline: solo la risoluzione di
dati freschi della catena tocca la rete.
Con la CLI
certificate build prende la lista delle foglie, i bersagli (hex grezzo di una
foglia, oppure file di cui calcolare l'hash) e la transazione di cui risolve i
dati di ancoraggio. certificate verify riesegue la prova di inclusione per ogni
elemento e stampa l'ancoraggio che ti resta da confermare on-chain:
# Build a certificate for two files against a published root.
cardanowall certificate build \
--leaves-list leaves.cbor \
--tx <tx-hash> \
--file contract.pdf --file exhibit-a.png \
--out contract.cert.json
# Re-verify the proofs offline — no network, no trust in the producer.
cardanowall certificate verify contract.cert.jsonIl codice di uscita 0 significa che la prova di ogni elemento si ricalcola fino
alla radice; un codice diverso da zero segnala un fallimento di inclusione, un
input errato o un errore di I/O, perciò si inserisce dritto in CI. Ogni singolo
elemento può anche essere estratto nella forma canonica
{ tree_alg, tree_size, index, leaf, proof[] } e controllato con
cardanowall merkle verify.
Con l'SDK TypeScript
L'API certificate è pura: recuperi i byte della lista delle foglie con il
fetch proprio della piattaforma e glieli passi; il percorso crittografico non
raggiunge mai la rete:
import { certificate, merkle } from '@cardanowall/sdk-ts';
// `leaves` comes from decodeLeavesList(...) over the fetched leaves-list bytes.
const cert = certificate.buildInclusionCertificate({
anchor, // chain facts resolved from the tx
merkle: { treeAlg: 'rfc9162-sha256', root, treeSize: leaves.length },
leaves,
targets: [{ leaf, leafAlg: 'sha2-256', label: 'contract.pdf' }],
});
// Pure re-verification from the certificate alone — no Arweave, no chain.
const result = certificate.verifyInclusionCertificate(cert);
console.log(result.ok); // true when every item's proof recomputes to root
console.log(result.anchorClaim); // the anchor you confirm on a public explorer
// Each item also re-verifies through the plain Merkle predicate.
const item = cert.items[0];
const ok = merkle.merkleSha2256VerifyInclusion(
leafBytes, item.index, cert.merkle.tree_size, proofBytes, rootBytes,
);verifyInclusionCertificate riporta il verdetto della prova e fa da eco
all'ancoraggio affermato; confermare quell'ancoraggio on-chain è il tuo
passaggio separato ed esplicito, e l'oggetto risultato lo dice. Lo stesso modulo è
rispecchiato byte per byte negli SDK Python (certificate) e Rust
(certificate).
Nel browser, su una pagina di transazione
Un record della label 309 che porta un impegno merkle[] mostra un pannello di
inclusione sulla sua pagina di transazione. Incolla uno o più hash in hex, oppure
trascina i file originali per calcolarne l'hash lato client; la pagina recupera la
lista delle foglie direttamente dall'archiviazione indirizzata al contenuto nel
tuo browser, ricalcola ogni prova, mostra un verdetto verde/rosso per ciascun
elemento e offre il JSON, il CBOR e un PDF stampabile da scaricare. Il PDF
incorpora il JSON completo come allegato, perciò è esso stesso l'artefatto
verificabile dalla macchina, non solo l'immagine di uno. Niente di tutto ciò tocca
un server privato.
L'algoritmo di verifica, dall'inizio alla fine
Per verificare un certificato in modo indipendente —nel tuo codice, senza alcuno strumento da parte nostra— fai esattamente questo:
- Rifiuta i campi malformati. Ogni voce di
root/leaf/proof[]deve essere hex di lunghezza pari che decodifica a 32 byte;tree_sizee ogniindexdevono essere interi sicuri conindex < tree_sizee1 ≤ tree_size ≤ 2³² − 1. - Ricalcola la radice di ogni elemento. Usando RFC 9162 §2.1.3.2, ripiega la
foglia (
leaf = SHA-256(0x00 ‖ leaf_digest)) con il suo percorso di fratelli (node = SHA-256(0x01 ‖ L ‖ R)), suddividendo alla maggiore potenza di due strettamente al di sotto della dimensione del sottoalbero in corso, e confronta il risultato conmerkle.rootbyte per byte. Un albero a foglia singola ha una prova vuota. - Conferma l'ancoraggio on-chain. Recupera
anchor.tx_hashsu qualsiasi explorer pubblico di Cardano, leggi i suoi metadati della label 309 e conferma chemerkle.rootsia uguale almerkle[].rootdel record. L'ora di blocco che leggi lì è la marca temporale che il certificato afferma.
I passaggi 1 e 2 sono la parte autocontenuta: non lasciano mai la tua macchina. Il passaggio 3 è l'unica lettura di rete, e va a un explorer scelto da te, mai a chi ha prodotto il certificato.
Un file, verificabile per sempre
Poiché ogni fratello sul percorso è incorporato, un certificato continua a verificarsi a lungo dopo che la lista delle foglie, il gateway originale o il produttore non ci sono più. I due controlli —ricalcolare la radice dal file, confermare la radice on-chain— sono tutto ciò di cui chiunque avrà mai bisogno. Vedi Batch con Merkle per come la radice e la lista delle foglie vengono costruite in primo luogo, e Verifica per il modello completo di verificatore in cui si inserisce il controllo lato catena.