Anleitungen · Teil 5 von 6
Stapelweise mit Merkle
Manchmal haben Sie nicht eine Datei, sondern tausend. Einen Ordner voller Dokumente, einen Strom von Ereignissen, die Audit-Log-Zeilen eines ganzen Tages. Jedes Element in einer eigenen Transaktion zu verankern wäre verschwenderisch. Hashen Sie stattdessen jeden Eintrag zu einem Blatt, falten Sie die Blätter zu einer einzigen Merkle-Wurzel und veröffentlichen Sie nur diese eine Wurzel. Die geordnete Blätterliste bleibt off-chain; später lässt sich die Aufnahme eines einzelnen Elements in die Menge mit einem Beweis nachweisen, der nur logarithmisch mit der Stapelgröße wächst.
Das Wesentliche: Den Baum zu erstellen und Beweise zu verifizieren ist vollständig offline möglich. Kein Gateway, kein Konto, kein Netzwerk. Allein das Veröffentlichen der Wurzel berührt ein Gateway; alles andere ist reine Berechnung, die Sie überall und jederzeit ausführen können.
Mit dem CLI
Übergeben Sie merkle build die Dateien, die Sie verankern möchten. Es hasht jede
zu einem Blatt, erstellt die RFC 9162-Wurzel
und gibt die kanonische Blätterliste aus:
cardanowall merkle build --file a.pdf --file b.pdf --file c.pdf --jsonSie können auch vorberechnete Blätter übergeben (einen 64-stelligen SHA-256-Digest
in Hex pro Zeile), über stdin oder per --in leaves.txt. Bewahren Sie die root
auf (sie wird veröffentlicht) ebenso wie die Blätterliste (alle, die später eine
Aufnahme nachweisen müssen, werden sie benötigen).
Um nachzuweisen, dass ein Element zu einer veröffentlichten Wurzel gehört, schreiben Sie eine kleine Proof-Datei mit dem Prüfpfad und verifizieren sie, vollständig offline und gegen jede Wurzel, der Sie vertrauen:
cardanowall merkle verify --root <root-hex> --proof proof.jsonDie Struktur von proof.json ist { tree_alg, tree_size, index, leaf, proof[] };
mit --leaf <hex> überschreiben Sie das aus der Datei abgeleitete Blatt. Der
Exit-Code 0 bedeutet, das Blatt liegt im Baum, 1 bedeutet, es ist nicht
enthalten. Damit lässt es sich direkt in CI einbinden.
Mit dem TypeScript-SDK
Hashen Sie Ihre Elemente zu Blättern, erstellen Sie Wurzel und Beweise lokal und veröffentlichen Sie dann nur die Wurzel über ein Gateway:
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); // truemerkleSha2256VerifyInclusion(leaf, index, treeSize, proof, root) ist ein reines
boolesches Prädikat. Es stellt nie eine Netzwerkverbindung her und wirft bei einem
ungültigen Beweis keine Ausnahme, sondern gibt schlicht false zurück. Wer die
Blätterliste besitzt, kann die Wurzel neu berechnen und jeden Beweis neu ableiten;
der Aussteller ist dabei nie beteiligt.
Die Wurzel zu veröffentlichen ist der einzige Schritt, der ein Gateway braucht. Holen Sie dafür ein Angebot ein und veröffentlichen Sie dann die Blätter: Das SDK berechnet die Wurzel lokal, das Gateway speichert die Blätterliste und verankert die Festlegung on-chain.
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);Mit dem Python-SDK
Der byteidentische Zwilling erstellt denselben Baum und dieselben Beweise:
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) # TrueMit dem Rust-SDK
Das cardanowall-Crate erstellt denselben Baum und dieselben Beweise offline und
veröffentlicht dann nur die Wurzel über den Gateway-Client:
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(())
}Eine Wurzel, viele Elemente, kein Vertrauen nötig
Die Wurzel legt sich auf jedes Blatt und dessen Position fest. Monate später, allein mit der Blätterliste und der on-chain verankerten Wurzel, kann jede und jeder einen Beweis neu ableiten und bestätigen, dass ein Element im Stapel enthalten war, ganz ohne Ihr Gateway, Ihren Server oder Ihre Mitwirkung. Wie Blätter gehasht werden, beschreibt Inhalt und Hashing; den Ablauf für ein einzelnes Element zeigt Veröffentlichen Sie Ihren ersten PoE.