Guides

Guides · Partie 5 sur 6

Traitement par lots avec Merkle

Parfois, vous n’avez pas un seul fichier : vous en avez mille. Un dossier de documents, un flux d’événements, les lignes d’une journée de journal d’audit. Ancrer chacun dans sa propre transaction est un gaspillage. Calculez plutôt l’empreinte de chaque élément pour en faire une feuille, repliez les feuilles en une unique racine de Merkle et publiez cette seule racine. Les feuilles ordonnées restent hors de la chaîne ; plus tard, vous pourrez prouver qu’un élément quelconque faisait partie de l’ensemble avec une preuve qui ne croît qu’avec le logarithme de la taille du lot.

Le point essentiel : la construction de l’arbre et la vérification des preuves sont entièrement hors ligne. Aucune passerelle, aucun compte, aucun réseau. Seule la publication de la racine recourt à une passerelle ; tout le reste est du calcul pur que vous pouvez exécuter n’importe où, pour toujours.

Avec la CLI

Passez à merkle build les fichiers que vous souhaitez ancrer. Elle calcule l’empreinte de chacun pour en faire une feuille, construit la racine RFC 9162 et émet la liste canonique des feuilles :

cardanowall merkle build --file a.pdf --file b.pdf --file c.pdf --json

Vous pouvez aussi fournir des feuilles précalculées — un résumé SHA-256 de 64 caractères hexadécimaux par ligne — sur l’entrée standard ou via --in leaves.txt. Conservez la root (publiez-la) et la liste des feuilles (elle sera utile à quiconque devra prouver l’inclusion par la suite).

Pour prouver qu’un élément appartient à une racine publiée, rédigez un petit fichier de preuve avec le chemin d’audit et vérifiez-le — entièrement hors ligne, par rapport à n’importe quelle racine en laquelle vous avez confiance :

cardanowall merkle verify --root <root-hex> --proof proof.json

La structure de proof.json est { tree_alg, tree_size, index, leaf, proof[] } ; passez --leaf <hex> pour remplacer la feuille du fichier. Le code de sortie 0 signifie que la feuille est dans l’arbre, 1 qu’elle ne l’est pas — intégrez-le directement dans votre CI.

Avec le SDK TypeScript

Calculez l’empreinte de vos éléments pour en faire des feuilles, construisez la racine et les preuves en local, puis publiez uniquement la racine via une passerelle :

import { Label309Client, hash, merkle } from '@cardanowall/sdk-ts';

const items = [docA, docB, docC]; // Uint8Array content
const leaves = items.map((bytes) => hash.sha2256(bytes));

const root = merkle.merkleSha2256Root(leaves);

// Prove item 1 is in the set — no network, no gateway.
const proof = merkle.merkleSha2256InclusionProof(leaves, 1);
const ok = merkle.merkleSha2256VerifyInclusion(leaves[1], 1, leaves.length, proof, root);
console.log(ok); // true

merkleSha2256VerifyInclusion(leaf, index, treeSize, proof, root) est un prédicat booléen pur : il n’accède jamais au réseau et ne lève jamais d’exception sur une preuve incorrecte, il renvoie simplement false. Quiconque détient la liste des feuilles peut recalculer la racine et redériver n’importe quelle preuve ; le publicateur n’intervient jamais dans le processus.

Publier la racine est l’unique étape qui nécessite une passerelle. Demandez-en le devis, puis publiez les feuilles — le SDK calcule la racine en local, la passerelle stocke la liste des feuilles et ancre l’engagement sur la chaîne :

const client = new Label309Client({
  baseUrl: 'https://your-gateway.example',
  apiKey: process.env.CW_API_KEY,
});

const quote = await client.poe.quote({
  recordBytes: 512,
  recipientCount: 0,
  fileBytesTotal: leaves.length * 32,
});

const published = await client.poe.publishMerkle({
  leaves, // raw 32-byte digests or hex strings
  quoteId: quote.quote_id,
});

console.log(published.root, published.leaf_count, published.tx_hash, published.ar_uri);

Avec le SDK Python

Le jumeau octet pour octet construit le même arbre et les mêmes preuves :

import cardanowall

items = [doc_a, doc_b, doc_c]  # bytes
leaves = [cardanowall.hash.sha2_256(b) for b in items]

root = cardanowall.merkle.merkle_sha2_256_root(leaves)

proof = cardanowall.merkle.merkle_sha2_256_inclusion_proof(leaves, 1)
ok = cardanowall.merkle.merkle_sha2_256_verify_inclusion(
    leaves[1], 1, len(leaves), proof, root
)
print(ok)  # True

Avec le SDK Rust

Le crate cardanowall construit le même arbre et les mêmes preuves hors ligne, puis publie uniquement la racine via le client de la passerelle :

use cardanowall::client::{
    Label309Client, Label309ClientConfig, MerkleLeaf, PublishMerkleInput, QuoteInput,
};
use cardanowall::{hash, merkle};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let items: Vec<Vec<u8>> = vec![doc_a, doc_b, doc_c]; // content bytes
    let leaves: Vec<[u8; 32]> = items.iter().map(|b| hash::sha256(b)).collect();

    // Build the root and prove inclusion — pure computation, no network.
    let root = merkle::merkle_root(&leaves)?;
    let proof = merkle::merkle_inclusion_proof(&leaves, 1)?;
    let ok = merkle::verify_inclusion(&leaves[1], 1, leaves.len(), &proof, &root);
    println!("{ok}"); // true

    // Publishing the root is the one step that needs a gateway.
    let client = Label309Client::new(Label309ClientConfig {
        base_url: Some("https://your-gateway.example".into()),
        api_key: std::env::var("CW_API_KEY").ok(),
    })?;

    let quote = client.poe().quote(&QuoteInput {
        record_bytes: 512,
        recipient_count: 0,
        file_bytes_total: (leaves.len() * 32) as u64,
    })?;

    let published = client.poe().publish_merkle(&PublishMerkleInput {
        leaves: leaves.iter().map(|l| MerkleLeaf::Bytes(l.to_vec())).collect(),
        quote_id: quote.quote_id,
        hash_alg: None,
        signer: None,
        idempotency_key: None,
        chunk_bytes: None,
    })?;

    println!("{} {} {:?}", published.root, published.leaf_count, published.tx_hash);
    Ok(())
}

Une racine, de nombreux éléments, aucune confiance requise

La racine s’engage sur chaque feuille et sur sa position. Des mois plus tard — avec la seule liste des feuilles et la racine ancrée sur la chaîne — quiconque peut redériver une preuve et confirmer qu’un élément faisait partie du lot, sans votre passerelle, sans votre serveur et sans votre coopération. Consultez Contenu et hachage pour savoir comment les feuilles sont hachées, et Publiez votre première PoE pour le flux à élément unique.