Anleitungen · Teil 5 von 8
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.
Die Wurzel zu veröffentlichen ist der einzige Schritt, der das Netzwerk berührt.
Übergeben Sie submit --merkle die Blätterliste, die merkle build ausgegeben
hat (oder eine schlichte Datei mit einem 64-stelligen Hex-Blatt pro Zeile); das
Gateway verankert die Wurzel und speichert die Liste. Eine Merkle-Veröffentlichung
kann einen früheren Datensatz auch direkt ablösen: Mit --supersedes <tx-hash> markieren Sie diesen Stapel als Nachfolger einer früheren Wurzel, etwa
eines neu gebündelten Ordners oder eines korrigierten Tages an Log-Zeilen:
cardanowall submit \
--merkle ./leaves.txt \
--supersedes <tx-hash> \
--base-url https://your-gateway.example \
--api-key "$CW_API_KEY"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.
publishMerkle bepreist die Veröffentlichung selbst: Es misst den exakten Datensatz,
schreibt unter Ihrer maxUsdMicros-Obergrenze einen Preis fest, lädt die
Blätterliste hoch und verankert die Festlegung on-chain. Das SDK berechnet die Wurzel
lokal; Sie schätzen nie die Datensatzgröße und schreiben nie von Hand ein Angebot
fest:
const client = new Label309Client({
baseUrl: 'https://your-gateway.example',
apiKey: process.env.CW_API_KEY,
});
const published = await client.poe.publishMerkle({
leaves, // raw 32-byte digests or hex strings
leafAlg: 'sha2-256', // advisory: names how the leaves were computed
maxUsdMicros: 2_000_000n, // refuse to spend more than $2
});
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,
};
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. `publish_merkle`
// quotes the exact record size internally and enforces the price cap.
let client = Label309Client::new(Label309ClientConfig {
base_url: Some("https://your-gateway.example".into()),
api_key: std::env::var("CW_API_KEY").ok(),
})?;
let published = client.poe().publish_merkle(
&PublishMerkleInput::new(leaves.iter().map(|l| MerkleLeaf::Bytes(l.to_vec())).collect())
.with_leaf_alg("sha2-256") // advisory: names how the leaves were computed
.with_max_usd_micros(2_000_000), // refuse to spend more than $2
)?;
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.