Guías · Parte 6 de 6
Certificados de inclusión
Has publicado una raíz de Merkle bajo la label 309, junto con la lista de hojas fuera de la cadena que la respalda. Esa raíz es un compromiso perfectamente válido, pero no es algo que puedas entregarle a un colega, adjuntar a un contrato o presentar en un expediente judicial. Un certificado de inclusión es ese elemento entregable: un pequeño archivo autocontenido que fija una o más hojas a la raíz publicada, incorpora cada hermano necesario para volver a derivar esa raíz y nombra la transacción de Cardano cuya hora de bloque da fe de todo el conjunto. Cualquiera puede verificarlo sin conexión, contra cualquier explorador, sin cuenta y sin confiar en quien lo haya producido.
Si conoces OpenTimestamps, es la misma idea —un recibo portátil de prueba de
existencia— con dos diferencias deliberadas. La autoridad de marca temporal es la
hora de bloque de la cadena de Cardano, no un servidor de calendario. Y la
prueba se genera y se verifica enteramente del lado del cliente: sin pasarela,
sin servidor emisor, sin confianza en nosotros en ningún paso. Es el equivalente,
en clave de prueba de existencia, de un archivo .ots, anclado en Cardano y
verificable de forma autónoma.
Qué demuestra un certificado, y qué no
Un certificado hace exactamente dos afirmaciones criptográficas, cada una comprobable de forma independiente por cualquiera:
- Inclusión. Una
leafdada ocupa la posiciónindexde un árbol de Merkle SHA-256 según RFC 9162 de tamañotree_sizecuya raíz esroot. Esto se demuestra recalculando la raíz a partir de la hoja, su índice, el tamaño del árbol y la ruta de hermanos incorporada. Es autocontenido: basta con el propio archivo del certificado. - Anclaje. Esa
rootaparece textualmente en el campomerkle[].rootdel registro de la label 309 que transporta la transaccióntx_hash. Esto se demuestra leyendo esa transacción en cualquier explorador público de Cardano y comparando los bytes. Necesita el archivo del certificado más un explorador, nada más.
Juntas demuestran que el contenido de la hoja existía en o antes de la hora de
bloque de tx_hash.
Qué NO demuestra un certificado
La hora la afirma la cadena de bloques pública, no queda ligada criptográficamente a la prueba —exactamente igual que con OpenTimestamps y Chainpoint, confías en la hora de bloque de la cadena, nunca en quien produjo el certificado—. Un certificado no es una marca temporal electrónica «cualificada» eIDAS (RFC 3161 / una TSA cualificada); es una marca temporal anclada en una cadena de bloques: una prueba corroborante sólida de una afirmación temporal, en la misma categoría que otras marcas temporales basadas en cadenas de bloques, y la redacción del archivo lo dice tal cual. No dice nada sobre quién es el autor del contenido (eso es una firma de registro opcional; consulta Firmas), ni demuestra que el contenido no se conociera antes. Demuestra existencia para una fecha límite, no autoría ni novedad.
Por qué la prueba no va firmada
Un Receipt COSE estricto del IETF es un COSE_Sign1 cuyo payload es la raíz de
Merkle, firmado por alguna autoridad. Aquí la autoridad es la cadena de
bloques, no una clave en nuestro poder, así que firmar la raíz con nuestra clave
reintroduciría confianza en el servidor y rompería la propiedad de verificabilidad
autónoma sobre la que descansa todo el estándar. En lugar de eso, el certificado
emite la estructura CBOR de prueba de inclusión del IETF exactamente como está
especificada, y lleva el anclaje en la cadena de bloques en lugar de la firma.
La matemática de la prueba es idéntica byte a byte a la codificación del IETF;
no va firmada de forma deliberada y está anclada en la cadena de bloques.
El certificado JSON
El artefacto principal es label-309-inclusion-certificate-v1: un archivo JSON
legible tanto para humanos como para máquinas. Un solo archivo cubre una o muchas
hojas, y cada elemento incorpora su ruta completa de hermanos, de modo que el
archivo se vuelve a verificar para siempre: sin recuperación desde Arweave, sin
pasarela, sin necesidad del publicador original.
{
"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"
}
}Algunos detalles que conviene conocer:
root,leafy cada entrada deproof[]son valores en bruto de 32 bytes representados en hex. Los productores los emiten en minúscula; un verificador acepta cualquiera de los dos casos y rechaza cualquier carácter no hexadecimal o cadena de longitud impar.- El
"verified": truealmacenado es el resultado del productor en el momento de la construcción. Un verificador nunca confía en él: recalcula la prueba por sí mismo y reporta su propio veredicto. Una hoja que no se encontró en el árbol se registra con"verified": falsey un campo"error", nunca se descarta en silencio, de modo que el archivo es honesto sobre los fallos. block_timees la marca temporal POSIX —afirmada por el explorador— del bloque que la incluye.block_time_isoes su representación en UTC, solo por comodidad.
La prueba de inclusión en CBOR (alineada con COSE / RFC 9162)
Junto al JSON, cada elemento puede exportarse como un artefacto .cbor compacto
cuya estructura de prueba es idéntica byte a byte a
draft-ietf-cose-merkle-tree-proofs.
Esto significa que cualquier verificador de estructuras de datos verificables
RFC 9162 / COSE puede leer la matemática de la prueba directamente: el núcleo de
interoperabilidad es estándar, no a medida. No transporta ninguna hora de bloque
absoluta ni prosa jurídica (eso vive en el JSON); es únicamente el núcleo portátil
de la prueba, y está anclado en la cadena de bloques y sin firmar por la razón
expuesta más arriba.
La prueba de inclusión escueta del IETF —bstr .cbor [tree_size, leaf_index, inclusion_path], con el valor vds (verifiable-data-structure) 1 para
RFC 9162 SHA-256— se puede extraer por sí sola para un verificador COSE puro; el
anclaje en Cardano se transporta junto a ella como un pequeño mapa en lugar de la
firma Sign1.
Construir y verificar con las herramientas
El formato de certificado forma parte de las herramientas públicas e
independientes de la pasarela: el SDK @cardanowall/sdk-ts (con gemelos idénticos
byte a byte en Python y Rust), la CLI cardanowall y las superficies de
verificación de las aplicaciones. La matemática de construcción y verificación es
pura y sin conexión: solo la resolución de datos frescos de la cadena toca la red.
Con la CLI
certificate build toma la lista de hojas, los objetivos (hex de hoja en bruto, o
archivos a los que calcular el hash) y la transacción cuyos datos de anclaje
resuelve. certificate verify vuelve a ejecutar la prueba de inclusión por cada
elemento e imprime el anclaje que todavía te toca confirmar en cadena:
# 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.jsonEl código de salida 0 significa que la prueba de cada elemento se recalcula
hasta la raíz; un código distinto de cero señala un fallo de inclusión, una
entrada incorrecta o un error de E/S, así que encaja directamente en CI. Cada
elemento individual también puede extraerse a la forma canónica
{ tree_alg, tree_size, index, leaf, proof[] } y comprobarse con
cardanowall merkle verify.
Con el SDK de TypeScript
La API certificate es pura: recuperas los bytes de la lista de hojas con el
fetch propio de la plataforma y se los pasas; la ruta criptográfica nunca llega
a la red:
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 reporta el veredicto de la prueba y devuelve como eco
el anclaje afirmado; confirmar ese anclaje en cadena es tu paso aparte y
explícito, y el objeto de resultado lo dice así. El mismo módulo se replica byte a
byte en los SDK de Python (certificate) y Rust (certificate).
En el navegador, en una página de transacción
Un registro de la label 309 que lleve un compromiso merkle[] muestra un panel de
inclusión en su página de transacción. Pega uno o más hashes en hex, o suelta los
archivos originales para calcular su hash del lado del cliente; la página recupera
la lista de hojas directamente desde el almacenamiento direccionado por contenido
en tu navegador, recalcula cada prueba, muestra un veredicto verde/rojo por
elemento y ofrece el JSON, el CBOR y un PDF imprimible para descargar. El PDF
incorpora el JSON completo como un archivo adjunto, así que él mismo es el
artefacto verificable por máquina, no solo una imagen de uno. Nada de esto toca un
servidor privado.
El algoritmo de verificación, de principio a fin
Para verificar un certificado de forma independiente —en tu propio código, sin ninguna herramienta nuestra— haz exactamente esto:
- Rechaza los campos malformados. Cada entrada de
root/leaf/proof[]debe ser hex de longitud par que decodifique a 32 bytes;tree_sizey cadaindexdeben ser enteros seguros conindex < tree_sizey1 ≤ tree_size ≤ 2³² − 1. - Recalcula la raíz de cada elemento. Usando RFC 9162 §2.1.3.2, pliega la
hoja (
leaf = SHA-256(0x00 ‖ leaf_digest)) con su ruta de hermanos (node = SHA-256(0x01 ‖ L ‖ R)), dividiendo en la mayor potencia de dos estrictamente por debajo del tamaño del subárbol en curso, y compara el resultado conmerkle.rootbyte a byte. Un árbol de una sola hoja tiene una prueba vacía. - Confirma el anclaje en cadena. Recupera
anchor.tx_hashen cualquier explorador público de Cardano, lee sus metadatos de la label 309 y confirma quemerkle.rootes igual almerkle[].rootdel registro. La hora de bloque que leas ahí es la marca temporal que afirma el certificado.
Los pasos 1 y 2 son la parte autocontenida: nunca salen de tu máquina. El paso 3 es la única lectura de red, y va a un explorador que tú eliges, nunca a quien produjo el certificado.
Un archivo, verificable para siempre
Como cada hermano de la ruta está incorporado, un certificado sigue verificándose mucho después de que la lista de hojas, la pasarela original o el productor hayan desaparecido. Las dos comprobaciones —recalcular la raíz a partir del archivo, confirmar la raíz en cadena— son todo lo que nadie necesita jamás. Consulta Lotes con Merkle para ver cómo se construyen la raíz y la lista de hojas en primer lugar, y Verificación para el modelo de verificador completo en el que encaja la comprobación del lado de la cadena.