cambio: OP_RETURN solo detecta presencia y tamaño, no muestra contenido

This commit is contained in:
Aitor
2026-06-02 18:24:24 +02:00
parent 788ed3bada
commit aa5838c010
2 changed files with 42 additions and 17 deletions
+13
View File
@@ -12,6 +12,19 @@ y el versionado sigue [Versionado Semántico](https://semver.org/lang/es/):
--- ---
## [1.2.1] — 2026-06-02
### Cambiado
- La herramienta de OP_RETURN pasa de decodificar y mostrar el contenido a
solo detectar su presencia y tamaño. Para auditar privacidad, lo relevante
es que una transacción contenga datos arbitrarios (revela uso de un
protocolo o servicio), no qué dicen esos datos. Además, la herramienta no
debe funcionar como visor de contenido arbitrario de la cadena. Ahora indica
cuántos outputs OP_RETURN hay y su tamaño en bytes, sin mostrar el contenido.
---
## [1.2.0] — 2026-06-01 ## [1.2.0] — 2026-06-01
### Añadido ### Añadido
+29 -17
View File
@@ -2586,7 +2586,7 @@
const tools = [ const tools = [
{id:"converter", label:"Conversor", icon:"⇄"}, {id:"converter", label:"Conversor", icon:"⇄"},
{id:"validator", label:"Validar dirección", icon:"✓"}, {id:"validator", label:"Validar dirección", icon:"✓"},
{id:"op_return", label:"OP_RETURN", icon:"📄"}, {id:"op_return", label:"OP_RETURN", icon:"🔍"},
{id:"psbt", label:"PSBT", icon:"🔍"}, {id:"psbt", label:"PSBT", icon:"🔍"},
{id:"rawtx", label:"Tx Raw", icon:"⬡"}, {id:"rawtx", label:"Tx Raw", icon:"⬡"},
{id:"verifysig", label:"Verificar firma", icon:"🔐"}, {id:"verifysig", label:"Verificar firma", icon:"🔐"},
@@ -2737,7 +2737,11 @@
); );
} }
// ── Decodificador OP_RETURN ──────────────────────────────────────────── // ── Detector OP_RETURN ─────────────────────────────────────────────────
// Indica SI hay datos arbitrarios y su tamaño, pero NO decodifica ni
// muestra el contenido. Para auditar privacidad lo relevante es QUE HAYA
// un OP_RETURN (revela uso de protocolo/servicio), no qué dice. Además,
// evita ser un visor de contenido arbitrario de la cadena.
function ToolOpReturn({base}) { function ToolOpReturn({base}) {
const {get} = useApi(base); const {get} = useApi(base);
const [txid, setTxid] = useState(""); const [txid, setTxid] = useState("");
@@ -2754,37 +2758,45 @@
if (!tx) throw new Error("Transacción no encontrada"); if (!tx) throw new Error("Transacción no encontrada");
const opReturns = tx.vout.filter(o=>o.scriptpubkey_type==="op_return"||o.scriptpubkey?.startsWith("6a")); const opReturns = tx.vout.filter(o=>o.scriptpubkey_type==="op_return"||o.scriptpubkey?.startsWith("6a"));
if (opReturns.length===0) { setErr("Esta transacción no contiene outputs OP_RETURN"); setLoading(false); return; } if (opReturns.length===0) { setErr("Esta transacción no contiene outputs OP_RETURN"); setLoading(false); return; }
const decoded = opReturns.map(o=>{ // Solo medimos el tamaño en bytes. NO decodificamos el contenido.
const detected = opReturns.map(o=>{
const hex = o.scriptpubkey?.slice(4)||""; const hex = o.scriptpubkey?.slice(4)||"";
let text = ""; const bytes = Math.floor(hex.length/2);
try { text = decodeURIComponent(escape(hex.match(/.{1,2}/g).map(b=>String.fromCharCode(parseInt(b,16))).join(""))); } catch {} return { bytes };
return { hex, text, value:o.value };
}); });
setResult({ txid:tx.txid, outputs:decoded }); setResult({ txid:tx.txid, count:opReturns.length, outputs:detected });
} catch(e) { setErr(e.message); } } catch(e) { setErr(e.message); }
setLoading(false); setLoading(false);
}; };
return ( return (
<Card> <Card>
<div style={{fontSize:"0.62rem",color:C.blue,fontFamily:"monospace",textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Decodificador OP_RETURN</div> <div style={{fontSize:"0.62rem",color:C.blue,fontFamily:"monospace",textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Detector OP_RETURN</div>
<div style={{fontSize:"0.65rem",color:C.t2,marginBottom:12}}>Los outputs OP_RETURN almacenan mensajes arbitrarios en la blockchain. Introduce un txid para leerlos.</div> <div style={{fontSize:"0.65rem",color:C.t2,marginBottom:12}}>Indica si una transacción contiene datos arbitrarios (OP_RETURN) y su tamaño. No muestra el contenido: para auditar privacidad lo relevante es que existan, no qué dicen.</div>
<div style={{display:"flex",gap:8,marginBottom:12}}> <div style={{display:"flex",gap:8,marginBottom:12}}>
<input value={txid} onChange={e=>setTxid(e.target.value)} onKeyDown={e=>e.key==="Enter"&&decode()} <input value={txid} onChange={e=>setTxid(e.target.value)} onKeyDown={e=>e.key==="Enter"&&decode()}
placeholder="txid de una transacción" placeholder="txid de una transacción"
style={{flex:1,background:C.bg,border:`1px solid ${C.borderBright}`,borderRadius:6,padding:"9px 12px",color:C.t1,fontFamily:"monospace",fontSize:"0.75rem",outline:"none"}} style={{flex:1,background:C.bg,border:`1px solid ${C.borderBright}`,borderRadius:6,padding:"9px 12px",color:C.t1,fontFamily:"monospace",fontSize:"0.75rem",outline:"none"}}
onFocus={e=>e.target.style.borderColor=C.blue} onBlur={e=>e.target.style.borderColor=C.borderBright} onFocus={e=>e.target.style.borderColor=C.blue} onBlur={e=>e.target.style.borderColor=C.borderBright}
/> />
<button onClick={decode} style={{padding:"9px 16px",background:C.blueMuted,border:`1px solid ${C.blue}40`,borderRadius:6,color:C.blue,fontFamily:"monospace",fontSize:"0.72rem",cursor:"pointer",fontWeight:700}}>{loading?"···":"LEER"}</button> <button onClick={decode} style={{padding:"9px 16px",background:C.blueMuted,border:`1px solid ${C.blue}40`,borderRadius:6,color:C.blue,fontFamily:"monospace",fontSize:"0.72rem",cursor:"pointer",fontWeight:700}}>{loading?"···":"DETECTAR"}</button>
</div> </div>
{err&&<div style={{padding:"8px 12px",background:C.redMuted,border:`1px solid ${C.red}30`,borderRadius:6,color:C.red,fontFamily:"monospace",fontSize:"0.72rem"}}>{err}</div>} {err&&<div style={{padding:"8px 12px",background:C.redMuted,border:`1px solid ${C.red}30`,borderRadius:6,color:C.red,fontFamily:"monospace",fontSize:"0.72rem"}}>{err}</div>}
{result&&result.outputs.map((o,i)=>( {result&&(
<div key={i} style={{padding:"12px 14px",background:C.bg,borderRadius:6,border:`1px solid ${C.border}`,marginTop:8}}> <div style={{padding:"12px 14px",background:C.bg,borderRadius:6,border:`1px solid ${C.border}`,marginTop:8}}>
<div style={{fontSize:"0.62rem",color:C.t2,fontFamily:"monospace",marginBottom:6}}>OP_RETURN #{i+1}</div> <div style={{fontSize:"0.8rem",color:C.amber,fontFamily:"monospace",marginBottom:8}}>
{o.text&&<div style={{fontSize:"0.8rem",color:C.green,fontFamily:"monospace",marginBottom:6,wordBreak:"break-all"}}>"{o.text}"</div>} Contiene {result.count} output{result.count>1?"s":""} OP_RETURN con datos arbitrarios.
<div style={{fontSize:"0.65rem",color:C.t2,fontFamily:"monospace",wordBreak:"break-all"}}>HEX: {o.hex||"(vacío)"}</div> </div>
{result.outputs.map((o,i)=>(
<div key={i} style={{fontSize:"0.65rem",color:C.t2,fontFamily:"monospace",marginTop:4}}>
OP_RETURN #{i+1}: {o.bytes} bytes de datos
</div>
))}
<div style={{fontSize:"0.6rem",color:C.t3,marginTop:10,lineHeight:1.5}}>
Se indica la presencia y el tamaño, no el contenido. Un OP_RETURN puede revelar qué protocolo o servicio se usó ese es el dato relevante para tu privacidad.
</div>
</div> </div>
))} )}
</Card> </Card>
); );
} }
@@ -3087,7 +3099,7 @@
<P>Herramientas criptográficas que funcionan completamente en el navegador. Ningún dato se envía a servidores externos.</P> <P>Herramientas criptográficas que funcionan completamente en el navegador. Ningún dato se envía a servidores externos.</P>
<Li><strong style={{color:C.t1}}>Conversor sat/BTC/EUR/USD</strong> conversión en tiempo real usando el precio de tu propio nodo. Sin APIs de terceros.</Li> <Li><strong style={{color:C.t1}}>Conversor sat/BTC/EUR/USD</strong> conversión en tiempo real usando el precio de tu propio nodo. Sin APIs de terceros.</Li>
<Li><strong style={{color:C.t1}}>Validador de dirección</strong> verifica el formato y tipo de cualquier dirección Bitcoin: Legacy, P2SH, Native SegWit o Taproot.</Li> <Li><strong style={{color:C.t1}}>Validador de dirección</strong> verifica el formato y tipo de cualquier dirección Bitcoin: Legacy, P2SH, Native SegWit o Taproot.</Li>
<Li><strong style={{color:C.t1}}>Decodificador OP_RETURN</strong> lee los mensajes almacenados en outputs OP_RETURN de cualquier transacción.</Li> <Li><strong style={{color:C.t1}}>Detector OP_RETURN</strong> indica si una transacción contiene datos arbitrarios y su tamaño, sin mostrar el contenido.</Li>
<Li><strong style={{color:C.t1}}>Validador PSBT</strong> verifica que una PSBT tiene formato correcto antes de firmarla. Para verificación criptográfica completa usa Sparrow o Bitcoin Core.</Li> <Li><strong style={{color:C.t1}}>Validador PSBT</strong> verifica que una PSBT tiene formato correcto antes de firmarla. Para verificación criptográfica completa usa Sparrow o Bitcoin Core.</Li>
<Li><strong style={{color:C.t1}}>Decodificador tx raw</strong> valida el formato hexadecimal de una transacción sin firmar.</Li> <Li><strong style={{color:C.t1}}>Decodificador tx raw</strong> valida el formato hexadecimal de una transacción sin firmar.</Li>
<Li><strong style={{color:C.t1}}>Verificador de firma</strong> valida el formato de una firma de mensaje Bitcoin. Para verificación criptográfica completa usa Bitcoin Core (<Code>verifymessage</Code>).</Li> <Li><strong style={{color:C.t1}}>Verificador de firma</strong> valida el formato de una firma de mensaje Bitcoin. Para verificación criptográfica completa usa Bitcoin Core (<Code>verifymessage</Code>).</Li>