fix: batch payment penaliza correctamente + textos coherentes; docs: fixtures verificados

This commit is contained in:
Aitor
2026-06-03 19:18:47 +02:00
parent 889ffeb8dd
commit b77d21fd84
2 changed files with 123 additions and 7 deletions
+8 -7
View File
@@ -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"}}>