Stellar testnetCircom · Groth16 · BN254Soroban

Veil

Pago privado no rastreable. A deposita 10 XLM en un pool y entrega una nota secreta; C retira esos 10 XLM presentando una prueba de conocimiento cero. Nadie puede vincular on-chain el depósito con el retiro, y la misma nota no se puede usar dos veces.

Depósitos
…
Retiros
…
XLM en el pool
…
Contrato

Cómo funciona

1
Depósito. Alice genera nullifier y secret, calcula commitment = Poseidon(nullifier, secret) y lo inserta en un Merkle tree on-chain pagando 10 XLM. Guarda la nota off-chain.
2
Retiro. Carol reconstruye el árbol desde los eventos, genera una prueba Groth16 de que conoce una hoja del árbol, y el contrato la verifica con las host functions BN254 de Soroban. Paga 10 XLM a Dave.
3
Sin doble gasto. La prueba expone nullifierHash = Poseidon(nullifier). El contrato lo marca como gastado y rechaza cualquier segundo intento.

Demo ejecutada en testnet

PasoTransacción
init (SAC de XLM, 10 XLM)528f08c2…c3c6a9
deposit · alice → pool4b1a171b…cff213
withdraw · carol firma, dave recibe (prueba ZK)188ac2f9…268347
withdraw con la misma notarechazado · Error(Contract, #5) NullifierSpent

Actividad on-chain cargando…

LedgerEventoDatoTx

Verifícalo tú

git clone https://github.com/NovaCorpAI/veil && cd veil && npm i
bash scripts/demo.sh        # deposit (alice) → nota → withdraw (carol → dave) → doble retiro rechazado

Lo que había que acertar: Poseidon on-chain idéntico a circomlib (Poseidon(1,2) verificado), y el orden c1‖c0 de las coordenadas G2 entre snarkjs y las host functions. Las dos cosas se validaron con tests antes del deploy.