Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -7,7 +7,6 @@ import re
|
|
| 7 |
import os
|
| 8 |
import tempfile
|
| 9 |
import base64
|
| 10 |
-
# No se usan fpdf en este ejemplo
|
| 11 |
|
| 12 |
# ---------------- Funciones de an谩lisis y grafo -------------------
|
| 13 |
|
|
@@ -120,23 +119,19 @@ def analizar_transaccion(tx_id):
|
|
| 120 |
if es_mixer:
|
| 121 |
if heuristic_mixer and es_mixer_blockchain:
|
| 122 |
mixer_message = (
|
| 123 |
-
"<p style='color: #FF5252;'><strong>Alerta de Mixer / CoinJoin:</strong> "
|
| 124 |
-
"La transacci贸n cumple criterios heur铆sticos y la API de blockchain.com la etiqueta como MIXER.</p>"
|
| 125 |
)
|
| 126 |
elif heuristic_mixer:
|
| 127 |
mixer_message = (
|
| 128 |
-
"<p style='color: #FF5252;'><strong>Alerta de Mixer / CoinJoin:</strong> "
|
| 129 |
-
"La transacci贸n cumple criterios heur铆sticos compatibles con un mixer.</p>"
|
| 130 |
)
|
| 131 |
elif es_mixer_blockchain:
|
| 132 |
mixer_message = (
|
| 133 |
-
"<p style='color: #FF5252;'><strong>Alerta de Mixer / CoinJoin:</strong> "
|
| 134 |
-
"La API de blockchain.com etiqueta esta transacci贸n como MIXER.</p>"
|
| 135 |
)
|
| 136 |
else:
|
| 137 |
mixer_message = (
|
| 138 |
-
"<p style='color: #4CAF50;'><strong>Sin indicios de mezcla:</strong> "
|
| 139 |
-
"La transacci贸n no presenta patrones de mixer.</p>"
|
| 140 |
)
|
| 141 |
|
| 142 |
btc_price = get_btc_price() or 0
|
|
@@ -221,6 +216,7 @@ def analizar_transaccion(tx_id):
|
|
| 221 |
fee_str = f"{fee:.8f} BTC"
|
| 222 |
fee_str += f" (${fee * btc_price:,.2f})" if btc_price else ""
|
| 223 |
|
|
|
|
| 224 |
reporte = f"""
|
| 225 |
<div style="padding: 20px; border-radius: 10px;">
|
| 226 |
<h3 style="color: {'#FF5252' if es_mixer else '#4CAF50'};">
|
|
@@ -232,13 +228,12 @@ def analizar_transaccion(tx_id):
|
|
| 232 |
<p><strong>Tama帽o:</strong> {size if size else 'N/A'} bytes</p>
|
| 233 |
<p><strong>Peso:</strong> {weight}</p>
|
| 234 |
<p><strong>Fee rate:</strong> {fee_rate_str}</p>
|
| 235 |
-
<hr>
|
| 236 |
<p>馃摜 <strong>Inputs:</strong> {num_inputs}</p>
|
| 237 |
{mixer_metrics}
|
| 238 |
-
<p
|
| 239 |
-
<p
|
| 240 |
-
<p
|
| 241 |
-
<p
|
| 242 |
{mixer_message}
|
| 243 |
<p style="font-size: 0.9em;">
|
| 244 |
Nota: La detecci贸n se basa en heur铆sticas y en informaci贸n adicional de blockchain.com. Se recomienda usar herramientas especializadas para un an谩lisis forense completo.
|
|
@@ -250,25 +245,23 @@ def analizar_transaccion(tx_id):
|
|
| 250 |
except Exception as e:
|
| 251 |
return f"鈿狅笍 Error durante el an谩lisis: {str(e)}", None, None
|
| 252 |
|
| 253 |
-
# ---------------- Funci贸n para mostrar el modal mediante HTML -------------------
|
| 254 |
-
|
| 255 |
def mostrar_modal(analysis_tuple):
|
| 256 |
"""
|
| 257 |
-
A partir de la tupla de an谩lisis, devuelve un HTML
|
| 258 |
"""
|
| 259 |
if not analysis_tuple:
|
| 260 |
return "<p>No hay an谩lisis generado.</p>"
|
| 261 |
report, _ = analysis_tuple
|
| 262 |
html_modal = f'''
|
| 263 |
-
<div id="
|
| 264 |
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 265 |
-
background: rgba(0,0,0,0.8); z-index: 9999;">
|
| 266 |
<div style="
|
| 267 |
-
position:
|
| 268 |
-
|
| 269 |
-
background: white; padding: 20px; max-height: 90%; overflow-y: auto;">
|
| 270 |
{report}
|
| 271 |
-
<br><br
|
|
|
|
| 272 |
style='padding: 5px 10px; font-size: 12px;'>Cerrar</button>
|
| 273 |
</div>
|
| 274 |
</div>
|
|
@@ -288,12 +281,13 @@ with gr.Blocks(
|
|
| 288 |
gr.Markdown("**Nota:** Este analizador funciona 煤nicamente con transacciones de Bitcoin. No se pueden analizar transacciones de Ethereum.")
|
| 289 |
|
| 290 |
with gr.Row():
|
| 291 |
-
# Columna izquierda: Campo de TXID,
|
| 292 |
with gr.Column(scale=1):
|
| 293 |
tx_input = gr.Textbox(
|
| 294 |
label="TXID de la Transacci贸n",
|
| 295 |
placeholder="Ej: 9dd51e2d45f4f7bddcc3f0f7a05c3fd60543a11cfc9fbd0e1ca4434668cfa3e1"
|
| 296 |
)
|
|
|
|
| 297 |
gr.Markdown("### Ejemplos de TXIDs v谩lidos:")
|
| 298 |
gr.Examples(
|
| 299 |
examples=[
|
|
@@ -328,16 +322,15 @@ with gr.Blocks(
|
|
| 328 |
with gr.Column(scale=1):
|
| 329 |
plot_output = gr.Plot()
|
| 330 |
reporte_html = gr.HTML()
|
| 331 |
-
analyze_btn = gr.Button("Analizar Transacci贸n", elem_classes=["custom-btn"])
|
| 332 |
modal_btn = gr.Button("GENERAR ANALISIS", elem_classes=["custom-btn"])
|
| 333 |
modal_output = gr.HTML()
|
| 334 |
|
| 335 |
# Estado para almacenar el an谩lisis (tupla: (reporte, fig))
|
| 336 |
analysis_state = gr.State()
|
| 337 |
|
| 338 |
-
# Al
|
| 339 |
analyze_btn.click(fn=analizar_transaccion, inputs=tx_input, outputs=[reporte_html, plot_output, analysis_state])
|
| 340 |
-
# Al
|
| 341 |
modal_btn.click(fn=mostrar_modal, inputs=analysis_state, outputs=modal_output)
|
| 342 |
|
| 343 |
-
demo.launch()
|
|
|
|
| 7 |
import os
|
| 8 |
import tempfile
|
| 9 |
import base64
|
|
|
|
| 10 |
|
| 11 |
# ---------------- Funciones de an谩lisis y grafo -------------------
|
| 12 |
|
|
|
|
| 119 |
if es_mixer:
|
| 120 |
if heuristic_mixer and es_mixer_blockchain:
|
| 121 |
mixer_message = (
|
| 122 |
+
"<p style='color: #FF5252;'><strong>Alerta de Mixer / CoinJoin:</strong> La transacci贸n cumple criterios heur铆sticos y la API de blockchain.com la etiqueta como MIXER.</p>"
|
|
|
|
| 123 |
)
|
| 124 |
elif heuristic_mixer:
|
| 125 |
mixer_message = (
|
| 126 |
+
"<p style='color: #FF5252;'><strong>Alerta de Mixer / CoinJoin:</strong> La transacci贸n cumple criterios heur铆sticos compatibles con un mixer.</p>"
|
|
|
|
| 127 |
)
|
| 128 |
elif es_mixer_blockchain:
|
| 129 |
mixer_message = (
|
| 130 |
+
"<p style='color: #FF5252;'><strong>Alerta de Mixer / CoinJoin:</strong> La API de blockchain.com etiqueta esta transacci贸n como MIXER.</p>"
|
|
|
|
| 131 |
)
|
| 132 |
else:
|
| 133 |
mixer_message = (
|
| 134 |
+
"<p style='color: #4CAF50;'><strong>Sin indicios de mezcla:</strong> La transacci贸n no presenta patrones de mixer.</p>"
|
|
|
|
| 135 |
)
|
| 136 |
|
| 137 |
btc_price = get_btc_price() or 0
|
|
|
|
| 216 |
fee_str = f"{fee:.8f} BTC"
|
| 217 |
fee_str += f" (${fee * btc_price:,.2f})" if btc_price else ""
|
| 218 |
|
| 219 |
+
# Se ha eliminado el <hr> para que la informaci贸n aparezca de forma continua.
|
| 220 |
reporte = f"""
|
| 221 |
<div style="padding: 20px; border-radius: 10px;">
|
| 222 |
<h3 style="color: {'#FF5252' if es_mixer else '#4CAF50'};">
|
|
|
|
| 228 |
<p><strong>Tama帽o:</strong> {size if size else 'N/A'} bytes</p>
|
| 229 |
<p><strong>Peso:</strong> {weight}</p>
|
| 230 |
<p><strong>Fee rate:</strong> {fee_rate_str}</p>
|
|
|
|
| 231 |
<p>馃摜 <strong>Inputs:</strong> {num_inputs}</p>
|
| 232 |
{mixer_metrics}
|
| 233 |
+
<p><strong>Total Entradas:</strong> {total_input_str}</p>
|
| 234 |
+
<p><strong>Total Salidas:</strong> {total_output_str}</p>
|
| 235 |
+
<p><strong>Fee:</strong> {fee_str}</p>
|
| 236 |
+
<p><strong>Estado de Confirmaci贸n:</strong> {"Confirmada" if status.get("confirmed", False) else "No confirmada"}</p>
|
| 237 |
{mixer_message}
|
| 238 |
<p style="font-size: 0.9em;">
|
| 239 |
Nota: La detecci贸n se basa en heur铆sticas y en informaci贸n adicional de blockchain.com. Se recomienda usar herramientas especializadas para un an谩lisis forense completo.
|
|
|
|
| 245 |
except Exception as e:
|
| 246 |
return f"鈿狅笍 Error durante el an谩lisis: {str(e)}", None, None
|
| 247 |
|
|
|
|
|
|
|
| 248 |
def mostrar_modal(analysis_tuple):
|
| 249 |
"""
|
| 250 |
+
A partir de la tupla de an谩lisis, devuelve un HTML que simula un modal emergente con el informe completo.
|
| 251 |
"""
|
| 252 |
if not analysis_tuple:
|
| 253 |
return "<p>No hay an谩lisis generado.</p>"
|
| 254 |
report, _ = analysis_tuple
|
| 255 |
html_modal = f'''
|
| 256 |
+
<div id="modalOverlay" style="
|
| 257 |
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 258 |
+
background: rgba(0,0,0,0.8); z-index: 9999; overflow: auto;">
|
| 259 |
<div style="
|
| 260 |
+
position: relative; margin: 5% auto; padding: 20px;
|
| 261 |
+
background: inherit; max-width: 800px; border-radius: 10px;">
|
|
|
|
| 262 |
{report}
|
| 263 |
+
<br><br>
|
| 264 |
+
<button onclick="document.getElementById('modalOverlay').remove();"
|
| 265 |
style='padding: 5px 10px; font-size: 12px;'>Cerrar</button>
|
| 266 |
</div>
|
| 267 |
</div>
|
|
|
|
| 281 |
gr.Markdown("**Nota:** Este analizador funciona 煤nicamente con transacciones de Bitcoin. No se pueden analizar transacciones de Ethereum.")
|
| 282 |
|
| 283 |
with gr.Row():
|
| 284 |
+
# Columna izquierda: Campo de TXID, ejemplos y explicaci贸n de campos
|
| 285 |
with gr.Column(scale=1):
|
| 286 |
tx_input = gr.Textbox(
|
| 287 |
label="TXID de la Transacci贸n",
|
| 288 |
placeholder="Ej: 9dd51e2d45f4f7bddcc3f0f7a05c3fd60543a11cfc9fbd0e1ca4434668cfa3e1"
|
| 289 |
)
|
| 290 |
+
analyze_btn = gr.Button("Analizar Transacci贸n", elem_classes=["custom-btn"])
|
| 291 |
gr.Markdown("### Ejemplos de TXIDs v谩lidos:")
|
| 292 |
gr.Examples(
|
| 293 |
examples=[
|
|
|
|
| 322 |
with gr.Column(scale=1):
|
| 323 |
plot_output = gr.Plot()
|
| 324 |
reporte_html = gr.HTML()
|
|
|
|
| 325 |
modal_btn = gr.Button("GENERAR ANALISIS", elem_classes=["custom-btn"])
|
| 326 |
modal_output = gr.HTML()
|
| 327 |
|
| 328 |
# Estado para almacenar el an谩lisis (tupla: (reporte, fig))
|
| 329 |
analysis_state = gr.State()
|
| 330 |
|
| 331 |
+
# Al pulsar "Analizar Transacci贸n", se generan reporte, grafo y se guarda el an谩lisis.
|
| 332 |
analyze_btn.click(fn=analizar_transaccion, inputs=tx_input, outputs=[reporte_html, plot_output, analysis_state])
|
| 333 |
+
# Al pulsar "GENERAR ANALISIS", se muestra un modal emergente con el informe completo.
|
| 334 |
modal_btn.click(fn=mostrar_modal, inputs=analysis_state, outputs=modal_output)
|
| 335 |
|
| 336 |
+
demo.launch()
|