fix: analisis de privacidad de direcciones se muestra en Lab (no callejon sin salida); texto adaptado a direccion/tx

This commit is contained in:
Aitor
2026-06-03 20:33:45 +02:00
parent 8d0c2620d3
commit d232f0bc0d
2 changed files with 19 additions and 10 deletions
+12 -1
View File
@@ -35,7 +35,7 @@ nodo y corrige la fila).
| # | Tipo | address | Resultado esperado | Verificado |
|---|------|---------|--------------------|------------|
| A | Máxima reutilización (Satoshi genesis) | `1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa` | Reutilización crítica, banda BAJA extrema | ☐ |
| 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 |
---
@@ -143,3 +143,14 @@ 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-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.
+7 -9
View File
@@ -1544,7 +1544,7 @@
const funded = addr.chain_stats ? addr.chain_stats.funded_txo_sum : 0;
checks.push({ id:"volume", label:"Volumen de actividad", certainty:"CERTEZA", pass:true, detail:`Total recibido: ${(funded/1e8).toFixed(4)} BTC en ${txCount} transacciones.`, penalty:0, informational:true });
score = Math.max(0, Math.min(100, score));
return { score, checks, summary: score >= 70 ? "Privacidad aceptable" : score >= 40 ? "Privacidad mejorable" : "Privacidad baja", summaryColor: score >= 70 ? C.green : score >= 40 ? C.amber : C.red };
return { score, checks, band: score>=75?"ALTA":score>=45?"MEDIA":"BAJA", summary: score >= 75 ? "Privacidad aceptable" : score >= 45 ? "Privacidad mejorable" : "Privacidad baja", summaryColor: score >= 75 ? C.green : score >= 45 ? C.amber : C.red, wallets: [] };
}
// ── fetch con timeout — protege el nodo y evita el spinner infinito ──
@@ -1811,7 +1811,9 @@
<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>=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."}
{tx
? (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.")
: (score>=75?"La dirección presenta buenas propiedades de privacidad.":score>=45?"La dirección es funcional, pero tiene características que reducen su privacidad.":"Esta direcció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"}}>
@@ -2647,7 +2649,7 @@
{err&&<div style={{padding:"10px 14px",background:C.redMuted,border:`1px solid ${C.red}30`,borderRadius:6,color:C.red,fontFamily:"monospace",fontSize:"0.75rem"}}>Error: {err}</div>}
{loading&&<Spinner/>}
{result&&result.type==="tx"&&<TxResult tx={result.data} onAnalyze={q=>{window.__setTab&&window.__setTab("auditoria",q);}}/>}
{result&&result.type==="address"&&<AddressResult addr={result.data} onAnalyze={q=>{window.__setTab&&window.__setTab("auditoria",q);}}/>}
{result&&result.type==="address"&&<AddressResult addr={result.data} analysis={analysis}/>}
{result&&result.type==="block"&&<BlockResult block={result.data}/>}
<UTXOMap base={base}/>
</div>
@@ -3458,7 +3460,7 @@
);
}
function AddressResult({addr, onAnalyze}) {
function AddressResult({addr, analysis}) {
const c=addr.chain_stats||{funded_txo_sum:0,spent_txo_sum:0,tx_count:0};
const m=addr.mempool_stats||{funded_txo_sum:0,spent_txo_sum:0};
const balance=c.funded_txo_sum-c.spent_txo_sum;
@@ -3475,12 +3477,8 @@
<Tag label="Transacciones" value={fmt.num(c.tx_count)} color={C.t1}/>
<Tag label="UTXOs" value={fmt.num(addr.utxos?addr.utxos.length:0)} color={C.amber}/>
</div>
{onAnalyze&&<div style={{marginTop:12,paddingTop:12,borderTop:`1px solid ${C.border}`}}>
<button onClick={()=>onAnalyze(addr.address)} style={{padding:"7px 16px",background:C.purpleMuted,border:`1px solid ${C.purple}40`,borderRadius:6,color:C.purple,fontFamily:"monospace",fontSize:"0.68rem",cursor:"pointer",fontWeight:700,letterSpacing:"0.1em"}}>
⌖ ANALIZAR PRIVACIDAD →
</button>
</div>}
</Card>
{analysis&&<PrivacyLab analysis={analysis}/>}
{addr.utxos&&addr.utxos.length>0&&<Card><div style={{fontSize:"0.62rem",color:C.t2,fontFamily:"monospace",textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:10}}>UTXOs</div>{addr.utxos.slice(0,6).map((u,i)=><div key={i} style={{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"6px 0",borderBottom:i<Math.min(addr.utxos.length,6)-1?`1px solid ${C.border}`:"none"}}><span style={{fontSize:"0.68rem",fontFamily:"monospace",color:C.blue}}>{fmt.hash(u.txid)}:{u.vout}</span><div style={{display:"flex",gap:6}}><Badge color={C.green}>{fmt.btc(u.value)}</Badge><Badge color={u.status&&u.status.confirmed?C.t2:C.amber}>{u.status&&u.status.confirmed?`#${fmt.num(u.status.block_height)}`:"mempool"}</Badge></div></div>)}</Card>}
{addr.txs&&addr.txs.length>0&&<Card><div style={{fontSize:"0.62rem",color:C.t2,fontFamily:"monospace",textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:10}}>Transacciones Recientes</div>{addr.txs.slice(0,8).map((tx,i)=>{const out=tx.vout?tx.vout.reduce((s,v)=>s+v.value,0):0;return<div key={i} style={{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"7px 0",borderBottom:i<Math.min(addr.txs.length,8)-1?`1px solid ${C.border}`:"none"}}><div><div style={{fontSize:"0.68rem",fontFamily:"monospace",color:C.blue}}>{fmt.hash(tx.txid)}</div><div style={{fontSize:"0.6rem",color:C.t2,marginTop:2}}>{tx.status&&tx.status.block_time?fmt.date(tx.status.block_time):"pendiente"}</div></div><div style={{display:"flex",gap:6}}><Badge color={tx.status&&tx.status.confirmed?C.green:C.amber}>{tx.status&&tx.status.confirmed?"conf":"pend"}</Badge><Badge color={C.t2}>{fmt.btc(out)}</Badge></div></div>;})}</Card>}
</div>