fix: batch payment penaliza correctamente + textos coherentes; docs: fixtures verificados
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
||||
# 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 | `323df21f0b0756f98336437aa3d2fb87e02b59f1946b714a7b09df04d429dec2` | CoinJoin detectado, banda ALTA, fingerprint "Samourai/Sparrow" inferido | ☐ |
|
||||
| 2 | WabiSabi CoinJoin | `fb596c9f675471019c60e984b569f9020dac3b2822b16396042b50c890b45e5e` | CoinJoin grande, banda ALTA, "Wasabi" inferido | ☐ |
|
||||
| 3 | JoinMarket CoinJoin | `4f112abd2eefe3484a7bbf7c1731f784cba19de677468835145e9c448fb18b7d` | CoinJoin con outputs iguales, verificar mínimo ~10k sats | ☐ |
|
||||
| 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 | Dust attack 555 sats | `655c533bf059721cec9d3d70b3171a07997991a02fedfa1c9b593abc645e1cc5` | Dust detectado con tipo correcto, no falso positivo, banda BAJA | ☐ |
|
||||
| 6 | Taproot + OP_RETURN | `0bf67b1f05326afbd613e11631a2b86466ac7e255499f6286e31b9d7d889cee7` | OP_RETURN detectado (presencia/tamaño), banda BAJA | ☐ |
|
||||
| 7 | Legacy P2PKH simple | `0b6461de422c46a221db99608fcbe0326e4f2325ebf2a47c9faf660ed61ee6a4` | Tipo legacy, fingerprint conservador, banda MEDIA/BAJA | ☐ |
|
||||
| 8 | Taproot script-path | `37777defed8717c581b4c0509329550e344bdc14ac38f71fc050096887e535c8` | Taproot reconocido | ☐ |
|
||||
| 9 | Bare multisig | `60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1` | Multisig reconocido | ☐ |
|
||||
| 10 | OP_RETURN con datos ASCII | `8bae12b5f4c088d940733dcd1455efc6a3a69cf9340e17a981286d3778615684` | OP_RETURN detectado (solo presencia/tamaño, **no** mostrar contenido) | ☐ |
|
||||
|
||||
## Direcciones
|
||||
|
||||
| # | Tipo | address | Resultado esperado | Verificado |
|
||||
|---|------|---------|--------------------|------------|
|
||||
| A | Máxima reutilización (Satoshi genesis) | `1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa` | Reutilización crítica, banda BAJA extrema | ☐ |
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
+8
-7
@@ -1024,7 +1024,7 @@
|
||||
input_reuse: 25, input_type_mixing: 14, output_type_mismatch: 8,
|
||||
round_numbers: 10, rbf: 5, peeling: 8,
|
||||
unnecessary_input: 12, dust: 8, change_detection: 10,
|
||||
wallet_fingerprint: 6, batch_payment: 4, entity_ofac: 20,
|
||||
wallet_fingerprint: 6, batch_payment: 45, entity_ofac: 20,
|
||||
};
|
||||
let deductions = 0;
|
||||
|
||||
@@ -1226,7 +1226,7 @@
|
||||
// Por ahora: informativo, sin penalización, para no dar un falso positivo.
|
||||
const isPeeling = tx.vin.length === 1 && tx.vout.length === 2 && !likelyCJ;
|
||||
checks.push({
|
||||
id:"peeling", label:"Patrón de pago simple (1 input, 2 outputs)", certainty:"informativo", pass:!isPeeling,
|
||||
id:"peeling", label:"Patrón de pago simple (peeling)", certainty:"informativo", pass:!isPeeling,
|
||||
actionability: "evitable",
|
||||
detail: isPeeling
|
||||
? "1 input y 2 outputs: el patrón de pago más común (un pago + un cambio). Por sí solo no es un problema. Solo se convierte en peeling chain si se encadena con otras transacciones iguales — eso no puede confirmarse mirando una transacción aislada."
|
||||
@@ -1388,14 +1388,15 @@
|
||||
const distinctRatio = tx.vout.length > 0 ? uniqueOutValues.size / tx.vout.length : 0;
|
||||
const isBatch = tx.vout.length >= 5 && distinctRatio >= 0.8 && uniqueOutValues.size >= 5 && !likelyCJ;
|
||||
checks.push({
|
||||
id:"batch_payment", label:"Pago por lotes (batch payment)", certainty:"informativo", pass:!isBatch,
|
||||
id:"batch_payment", label:"Pago por lotes (batch payment)", certainty:"PROBABLE", pass:!isBatch,
|
||||
actionability: "no_corregible",
|
||||
detail: isBatch
|
||||
? `${tx.vout.length} outputs, casi todos con valores distintos. Compatible con un exchange o servicio enviando a múltiples destinatarios en una sola transacción. Si eres uno de los receptores, esto no es un fallo tuyo.`
|
||||
? `${tx.vout.length} outputs, casi todos con valores distintos: compatible con un exchange o servicio que paga a muchos destinatarios en una sola transacción. Compartes los mismos inputs —y por tanto el mismo origen— con los otros destinatarios del lote, lo que permite a un observador agruparos.`
|
||||
: "No se detecta patrón de batch payment.",
|
||||
didactic: "Exchanges y custodios agrupan múltiples pagos en una transacción para ahorrar fees. Si recibes fondos de un batch, otros destinatarios podrían correlacionar sus pagos con los tuyos porque comparten los mismos inputs — y por tanto el mismo origen. No es algo que puedas controlar como receptor, pero conviene saberlo.",
|
||||
penalty: 0, informational: true,
|
||||
didactic: "Exchanges y custodios agrupan múltiples pagos en una transacción para ahorrar comisiones. Recibir de un batch reduce tu privacidad: los demás destinatarios comparten origen contigo, así que un observador puede correlacionar todos los pagos del lote. Y si el origen es un exchange con KYC, ese servicio conoce la identidad asociada. Por eso baja la valoración aunque tú no controles cómo te pagaron.",
|
||||
penalty: isBatch ? weights.batch_payment : 0,
|
||||
});
|
||||
if (isBatch) deductions += weights.batch_payment;
|
||||
|
||||
// ── 15. Timing analysis — informativo ────────────────────────────
|
||||
let timingDetail = "No hay timestamp disponible (transacción pendiente o sin confirmar).";
|
||||
@@ -1783,7 +1784,7 @@
|
||||
<div style={{fontSize:"0.7rem",color:C.t2,fontFamily:"monospace",marginBottom:4}}>NIVEL DE PRIVACIDAD</div>
|
||||
<div style={{fontSize:"1rem",color:summaryColor,fontFamily:"monospace",fontWeight:700,marginBottom:4}}>{summary}</div>
|
||||
<div style={{fontSize:"0.65rem",color:C.t2,lineHeight:1.5}}>
|
||||
{score>=70?"La transacción presenta buenas propiedades de privacidad.":score>=40?"Se detectaron características que reducen la privacidad.":"Esta transacción tiene características que facilitan su rastreo."}
|
||||
{score>=75?"La transacción presenta buenas propiedades de privacidad.":score>=45?"La transacción es funcional, pero tiene características que reducen su privacidad.":"Esta transacción tiene características que facilitan su rastreo."}
|
||||
</div>
|
||||
{wallets&&wallets.length>0&&(
|
||||
<div style={{marginTop:6,fontSize:"0.62rem",color:C.t2,fontFamily:"monospace"}}>
|
||||
|
||||
Reference in New Issue
Block a user