Files

13 KiB
Raw Permalink Blame History

Fixtures de validación — Txoko Node Dashboard

Conjunto de transacciones y direcciones reales conocidas para validar que el motor de análisis dice lo correcto en cada tipo de caso. Es la red de seguridad contra falsos positivos: antes de publicar un cambio en el motor, se pasan estos casos y se confirma que el resultado sigue siendo el esperado.

Cómo usarlo: analiza cada txid/dirección en la app (apuntando a tu propio nodo) y compara el resultado con la columna "Resultado esperado". Si algo no coincide, ahí hay un fallo (o el fixture estaba mal etiquetado: verifícalo en tu nodo y corrige la fila).

Nota: estos txids vienen del documento de diseño y conviene reverificarlos contra tu nodo la primera vez. Marca en la columna "Verificado" cuándo confirmaste cada uno. Un fixture con un txid equivocado es peor que no tenerlo.


Transacciones

# Tipo txid Resultado esperado Verificado
1 Whirlpool CoinJoin (5×5, 2019) 323df21f0b0756f98336437aa3d2fb87e02b59f1946b714a7b09df04d429dec2 CoinJoin detectado (✓ verde), banda ALTA, fingerprint "Samourai/Sparrow (Whirlpool)" 2026-06-03
2 WabiSabi CoinJoin fb596c9f675471019c60e984b569f9020dac3b2822b16396042b50c890b45e5e CoinJoin grande, banda ALTA, "Wasabi" inferido 2026-06-04
3 JoinMarket CoinJoin 4f112abd2eefe3484a7bbf7c1731f784cba19de677468835145e9c448fb18b7d CoinJoin con outputs iguales, verificar mínimo ~10k sats ⚠️ txid incorrecto — 2 inputs/4 outputs, no es JoinMarket inequívoco. Sustituir por txid real.
4 Batch payment 144 outputs (F2Pool, 2022-02-15) ef5d100a70eea9b7349a40fdfd51064fc5cebf5e3b4240f92591569836896949 1→144 outputs, NO CoinJoin, banda MEDIA, check "Pago por lotes" penaliza (45) 2026-06-03
5 Dusting de privacidad (555 sats, 2018) 655c533bf059721cec9d3d70b3171a07997991a02fedfa1c9b593abc645e1cc5 Salida de 555 sats detectada como "posible dusting" (POSIBLE), banda MEDIA, tono neutro 2026-06-03
6 Taproot + OP_RETURN 0bf67b1f05326afbd613e11631a2b86466ac7e255499f6286e31b9d7d889cee7 OP_RETURN detectado (presencia/tamaño), banda BAJA 2026-06-04
7 Legacy P2PKH simple 0b6461de422c46a221db99608fcbe0326e4f2325ebf2a47c9faf660ed61ee6a4 Tipo legacy, fingerprint conservador, banda MEDIA/BAJA 2026-06-04
8 Taproot script-path 37777defed8717c581b4c0509329550e344bdc14ac38f71fc050096887e535c8 Taproot reconocido 2026-06-04
9 Bare multisig 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1 Multisig reconocido 2026-06-04
10 OP_RETURN con datos ASCII 8bae12b5f4c088d940733dcd1455efc6a3a69cf9340e17a981286d3778615684 OP_RETURN detectado (solo presencia/tamaño, no mostrar contenido) 2026-06-04

Direcciones

# Tipo address Resultado esperado Verificado
A Máxima reutilización (Satoshi genesis) 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa Reutilización crítica (40), legacy (20), banda BAJA. Análisis se muestra en Lab 2026-06-03

Orden de privacidad esperado (coherencia global)

El motor debería ordenar la privacidad de forma coherente, de más a menos:

Whirlpool / WabiSabi  >  Taproot simple  >  Legacy simple  >  Batch  >  Dust / Satoshi

Si un batch sale con mejor banda que un Whirlpool, hay una incoherencia.


Matices de honestidad (no basta con que la banda sea correcta)

Un motor honesto no es un animador. Estos matices distinguen una herramienta seria de una que da palmaditas. Revisar que los textos los respetan:

Whirlpool / CoinJoin

  • Premia la mezcla (banda ALTA) pero sin vender humo: es alta privacidad en ESTA transacción, no anonimato permanente. Si luego se gastan mal las monedas (juntándolas, mandándolas a exchange KYC), se tira la mezcla.
  • El tamaño del anonymity set importa: no dar el mismo "ALTA" a una mezcla floja (pocos participantes) que a una buena.
  • Honestidad sobre lo que NO ve: en una sola tx no se sabe si eres quien entra o sale, ni qué pasó antes/después. El motor juzga la foto, no la historia (eso lo completa el rastro de procedencia).
  • No penalizar lo normal de un CoinJoin (outputs iguales, wallet identificado): es el objetivo, no un fallo.

Batch payment (de exchange)

  • Detectar que es un BATCH y no confundirlo con CoinJoin. Un CoinJoin mezcla para ocultar; un batch solo agrupa pagos. Si dice "CoinJoin" cuando es batch, el motor miente.
  • Banda MEDIA/BAJA.
  • Texto neutro: informar sin juzgar ni consolar.
    • Nada de "no es culpa tuya" (paternalista; un noderunner quiere el dato).
    • Sí dejar claro que los pagos vienen de un exchange/servicio (implica KYC, custodia de terceros).
    • Explicar el riesgo seco: se comparte origen con los otros destinatarios del lote, un observador puede agruparlos.

Dust attack

  • Detectar que es polvo enviado a propósito (cantidad mínima inesperada), marcar esa moneda, banda BAJA.
  • Explicar qué es y su consecuencia: gastarlo junto a otras monedas vincularía esa marca con el resto del wallet.
  • Tono neutro: informar la consecuencia, no aconsejar qué hacer. (La trampa del dust es que el usuario asustado lo gasta "para quitárselo" y se autovincula; el tono neutro informa sin dirigir y confía en que el usuario decide.)

Regla general

Txoko informa, no aconseja. Da el dato y la consecuencia; el usuario decide.


Registro de hallazgos (lo que han cazado los fixtures)

2026-06-03 — Fixture #4 (batch payment)

Al validar un batch real de 144 outputs (F2Pool) se encontraron tres fallos, todos corregidos:

  1. La banda daba ALTA a un batch (no penalizaba). Ahora el batch penaliza (peso 45) y un batch claro cae a MEDIA.
  2. El texto del check de batch decía "esto no es un fallo tuyo" (paternalista, contra la regla de diseño). Reescrito en tono neutro, explicando el riesgo real (compartir origen con los otros destinatarios del lote).
  3. La descripción de la banda decía "buenas propiedades de privacidad" aun cuando la banda era MEDIA (umbrales del texto descuadrados respecto a la banda). Alineados a 75/45.

También se observó (anotado, no bloqueante): el badge de certeza junto al título de cada check (CERTEZA/PROBABLE/POSIBLE) puede confundir cuando el check da resultado negativo —por ejemplo "Estructura CoinJoin / mezcla — PROBABLE" cuando en realidad NO hay CoinJoin (el resultado real está en el detalle, no en el badge). Pendiente de mejorar la claridad de la presentación.

2026-06-03 — Fixture #1 (Whirlpool CoinJoin)

Al validar un Whirlpool real (5×5) se confirmó que el motor lo detecta bien (banda ALTA, fingerprint correcto), pero el check de CoinJoin se mostraba con icono gris neutro y badge ámbar, como si fuera una advertencia — cuando detectar un CoinJoin es algo POSITIVO para la privacidad. Corregido: el icono y color de cada check ahora reflejan si el resultado es bueno o malo para la privacidad (verde = bueno), no solo el estado interno. Un CoinJoin detectado sale en verde. El badge de certeza se mantiene aparte, indicando solo el nivel de confianza. Esto resuelve en parte el punto anterior sobre la claridad del badge.

2026-06-03 — Fixture #5 (dust attack)

El txid del roadmap resultó ser un dusting real, pero con un matiz importante: la salida sospechosa era de 555 sats, justo POR ENCIMA del umbral técnico de dust (546). La app no lo detectaba, porque solo miraba el umbral técnico. Se encontraron y corrigieron dos cosas:

  1. Tono: el texto decía "trátalo como dust y no lo gastes" (consejo, contra la regla "Txoko informa, no aconseja"). Reescrito para informar la consecuencia y dejar decidir.
  2. Detección: se añadió un segundo nivel. Ahora el motor distingue dust técnico (< umbral, CERTEZA, es un hecho) de posible dusting de privacidad (umbral a 1000 sats, POSIBLE, es una sospecha — cantidades pequeñas pero gastables que un atacante usa a propósito para esquivar el umbral técnico). El caso de 555 sats ahora se detecta como "posible dusting" con certeza POSIBLE, sin afirmar de más.

Nota de proceso: en una iteración, el cambio se quedó en el archivo de trabajo pero no se copió bien al repo, y se presentó una versión sin el cambio. Lección: verificar con grep que el archivo presentado contiene el cambio antes de desplegar.

2026-06-04 — Fixture #2 (WabiSabi CoinJoin)

WabiSabi usa outputs de valor variable (a diferencia de Whirlpool con denominaciones fijas), por lo que isGenericCJ no lo detectaba. Se encontraron y corrigieron cuatro fallos:

  1. CoinJoin no detectado: el motor solo reconocía CoinJoin por outputs de igual valor. Se añadió una tercera variante (isStructuralCJ): muchos inputs Y outputs (≥10 cada uno), ratio inputs/outputs entre 0.5 y 2, y sin output dominante (< 30% del valor total). WabiSabi, JoinMarket y mezclas similares con outputs variables ahora se detectan.
  2. Checks erróneos en rojo: "Mezcla de tipos de script en inputs" e "Inputs innecesarios" aparecían con ✗ rojo en una mezcla. En un CoinJoin ambas condiciones son esperadas (participantes distintos aportan UTXOs de tipos y valores distintos). Ahora salen con punto gris e informativo cuando likelyCJ es true, sin penalización.
  3. Fingerprinting en rojo: "Fingerprinting de wallet" aparecía con ✗ rojo aunque no penalizara. En un CoinJoin identificar el software es informativo, no un problema. Ahora sale con punto gris cuando likelyCJ es true.
  4. Batch no suprimido: el check de batch requería !likelyCJ para suprimirse, pero como WabiSabi no se detectaba como CoinJoin, likelyCJ era false y el batch disparaba. Resuelto al corregir la detección (punto 1).

2026-06-04 — Fixtures #7, #8, #9, #10

#7 Legacy P2PKH (2016): el motor no tenía check para tipo de script legacy. Se añadió el check "Tipo de script legacy (P2PKH/P2SH)" (id: legacy_type, certeza CERTEZA, peso 15, actionability: wallet). Detecta inputs con tipo p2pkh o p2sh e informa que forman un conjunto de usuarios cada vez más pequeño. La banda queda en ALTA para una tx legacy limpia — decisión deliberada: calibrar la alarma al riesgo real, no exagerar. Un 15 informa sin alarmar.

#8 Taproot script-path (2021): sin cambios. Motor correcto — banda ALTA, fingerprint "Sparrow" razonable (Taproot + RBF + BIP69). Fixture cerrado.

#9 Bare multisig (2012): sin cambios. No se añadió check específico de bare multisig — caso marginal (formato obsoleto, prácticamente nadie lo usa) y el problema real de privacidad (reutilización de direcciones 25) ya está cubierto. Decisión: no añadir complejidad para un caso que no afecta al usuario tipo.

#10 OP_RETURN ASCII ("charley loves heidi", 2014): sin cambios necesarios. El check de OP_RETURN añadido en el fixture #6 funciona correctamente — detecta presencia y tamaño, banda BAJA, y no muestra el contenido ("charley loves heidi" visible en mempool.space pero no en Txoko). Comportamiento correcto.

2026-06-04 — Fixture #6 (Taproot + OP_RETURN)

La tx tiene un output OP_RETURN confirmado (visible en mempool.space), pero el motor no lo detectaba en el informe de privacidad. Se encontraron y corrigieron dos fallos:

  1. OP_RETURN no aparecía en el informe: isOpReturn existía solo para filtrar outputs gastables, pero nunca generaba un check. Se añadió el check "Datos OP_RETURN" (id: op_return, certeza CERTEZA, peso 30, no corregible) con detalle de número de outputs y bytes totales. No muestra el contenido — solo presencia y tamaño.
  2. Banda incorrecta (ALTA en lugar de BAJA): el peso 30 no era suficiente para bajar la banda porque el denominador (maxPossible) absorbe la penalización. Se añadió un cap explícito: cuando hasOpReturn es true, el score se limita a 44 (máximo de banda BAJA) independientemente del resto de señales.

Pendiente anotado (no bloqueante): el fingerprint dice "Wallet Taproot nativo" cuando el input es Multisig 2 de 2 P2SH. El motor está leyendo el tipo del output de cambio en lugar del input. Certeza POSIBLE — no afecta a la banda ni al análisis principal.

2026-06-03 — Fixture #A (dirección de Satoshi, reutilización extrema)

El motor clasificó bien la reutilización extrema (62.994 usos → reutilización 40, legacy 20, banda BAJA). Pero se encontraron dos fallos de cara al usuario:

  1. El botón "Analizar privacidad" del resultado de una dirección mandaba la dirección a la pestaña Auditoría, que solo acepta txids → callejón sin salida con error "introduce un txid válido". Corregido: el análisis de privacidad de la dirección (que ya se calculaba) ahora se muestra ahí mismo en Lab, con sus checks, reutilizando el componente PrivacyLab.
  2. El texto de la banda decía "Esta transacción..." aun analizando una dirección. Corregido: el texto se adapta (transacción / dirección) según lo analizado.