Guías · Parte 5 de 8
Lotes con Merkle
A veces no tiene un solo archivo: tiene mil. Una carpeta de documentos, un flujo de eventos, las entradas de un día en el registro de auditoría. Anclar cada uno en su propia transacción es un derroche. En su lugar, calcule el hash de cada elemento para obtener una hoja, pliegue las hojas en una única raíz de Merkle y publique esa única raíz. Las hojas ordenadas permanecen fuera de la cadena; más adelante podrá demostrar que cualquier elemento concreto estaba en el conjunto con una prueba que crece solo con el logaritmo del tamaño del lote.
Lo importante: construir el árbol y verificar las pruebas es totalmente sin conexión. Sin pasarela, sin cuenta, sin red. Solo la publicación de la raíz recurre a una pasarela; todo lo demás es cálculo puro que puede ejecutar en cualquier lugar, para siempre.
Con la CLI
Pase a merkle build los archivos que quiera anclar. Calcula el hash de cada
uno para obtener una hoja, construye la raíz RFC
9162 y emite la lista canónica de hojas:
cardanowall merkle build --file a.pdf --file b.pdf --file c.pdf --jsonTambién puede suministrar hojas precalculadas (un resumen SHA-256 de 64
caracteres hexadecimales por línea) por stdin o mediante --in leaves.txt.
Conserve la root (publíquela) y la lista de hojas (la querrá todo aquel que
luego necesite demostrar la inclusión).
Para demostrar que un elemento pertenece a una raíz publicada, escriba un pequeño archivo de prueba con la ruta de auditoría y verifíquelo, por completo sin conexión, contra cualquier raíz en la que confíe:
cardanowall merkle verify --root <root-hex> --proof proof.jsonLa forma de proof.json es { tree_alg, tree_size, index, leaf, proof[] }; pase
--leaf <hex> para sustituir la hoja del archivo. El código de salida 0
significa que la hoja está en el árbol, y 1 que no lo está: intégrelo
directamente en CI.
Publicar la raíz es el único paso que pasa por la red. Pase a submit --merkle
la lista de hojas que emitió merkle build (o un archivo sencillo con una hoja
de 64 hex por línea); la pasarela ancla la raíz y almacena la lista. Una
publicación de Merkle también puede reemplazar directamente un registro
anterior: pase --supersedes <tx-hash> para marcar este lote como el sucesor de
una raíz previa, por ejemplo una carpeta reagrupada o un día de registros de
auditoría corregido:
cardanowall submit \
--merkle ./leaves.txt \
--supersedes <tx-hash> \
--base-url https://your-gateway.example \
--api-key "$CW_API_KEY"Con el SDK de TypeScript
Calcule el hash de sus elementos para obtener las hojas, construya la raíz y las pruebas localmente, y luego publique solo la raíz a través de una pasarela:
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) es un
predicado booleano puro: nunca accede a la red ni lanza una excepción ante una
prueba incorrecta, simplemente devuelve false. Cualquiera que disponga de la
lista de hojas puede recalcular la raíz y volver a derivar cualquier prueba;
quien publicó nunca interviene en el proceso.
Publicar la raíz es el único paso que necesita una pasarela. publishMerkle fija
el precio de la publicación por sí mismo: mide el registro exacto, fija un precio
dentro de su límite maxUsdMicros, sube la lista de hojas y ancla el compromiso en
cadena. El SDK calcula la raíz localmente; usted nunca estima el tamaño del registro
ni fija una cotización a mano:
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);Con el SDK de Python
El gemelo byte por byte construye el mismo árbol y las mismas pruebas:
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) # TrueCon el SDK de Rust
El crate cardanowall construye el mismo árbol y las mismas pruebas sin
conexión, y luego publica solo la raíz a través del cliente de la pasarela:
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(())
}Una raíz, muchos elementos, cero confianza
La raíz se compromete con cada hoja y con su posición. Meses después, con solo la lista de hojas y la raíz anclada en la cadena, cualquiera puede volver a derivar una prueba y confirmar que un elemento estaba en el lote, sin su pasarela, sin su servidor y sin su cooperación. Consulte Contenido y hashing para saber cómo se calcula el hash de las hojas, y Publique su primera PoE para el flujo de un solo elemento.