leonett commited on
Commit
66f49d1
·
verified ·
1 Parent(s): 443319a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -17
app.py CHANGED
@@ -24,6 +24,20 @@ def get_transaction(tx_id):
24
  print(f"Excepción al consultar {url}: {e}")
25
  return None
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  def generar_grafo(tx_data):
28
  """
29
  Genera un grafo dirigido a partir de los datos de la transacción.
@@ -56,15 +70,15 @@ def analizar_transaccion(tx_id):
56
  """
57
  Analiza una transacción Bitcoin:
58
  - Obtiene los datos.
59
- - Aplica una heurística para detectar posibles mixers.
 
60
  - Genera un grafo interactivo.
61
  - Retorna un reporte en HTML y la figura del grafo.
62
 
63
- Se añaden datos forenses adicionales, como:
64
- - Totales en BTC de entradas y salidas.
65
- - Fee de la transacción.
66
- - Análisis de indicadores (cantidad de inputs/outputs, montos únicos).
67
- Nota: Este analizador se basa en heurísticas y se recomienda un análisis manual adicional.
68
  """
69
  tx_data = get_transaction(tx_id)
70
  if not tx_data:
@@ -72,25 +86,29 @@ def analizar_transaccion(tx_id):
72
  "Asegúrate de ingresar un TXID de Bitcoin válido."), None
73
 
74
  try:
75
- # Heurística para detectar mixer: muchos inputs/outputs y pocos montos únicos.
76
  num_inputs = len(tx_data.get("vin", []))
77
  num_outputs = len(tx_data.get("vout", []))
78
  montos = [out.get("value", 0) / 1e8 for out in tx_data.get("vout", [])]
79
  montos_unicos = len(set(montos))
80
 
81
- # Cálculos forenses adicionales: totales y fee
82
  total_input_value = sum(inp.get("prevout", {}).get("value", 0) for inp in tx_data.get("vin", [])) / 1e8
83
  total_output_value = sum(out.get("value", 0) for out in tx_data.get("vout", [])) / 1e8
84
  fee = total_input_value - total_output_value
85
 
86
- # Indicador de posible mixer según heurística simple.
87
  es_mixer = num_inputs > 5 and num_outputs > 5 and montos_unicos < 3
88
 
 
 
 
 
 
89
  # Generar grafo
90
  G = generar_grafo(tx_data)
91
  pos = nx.spring_layout(G, seed=42)
92
 
93
- # Datos de aristas
94
  edge_x, edge_y = [], []
95
  for edge in G.edges():
96
  x0, y0 = pos[edge[0]]
@@ -98,7 +116,6 @@ def analizar_transaccion(tx_id):
98
  edge_x.extend([x0, x1, None])
99
  edge_y.extend([y0, y1, None])
100
 
101
- # Datos de nodos
102
  node_x, node_y, hover_texts, node_colors, node_sizes = [], [], [], [], []
103
  for node in G.nodes():
104
  x, y = pos[node]
@@ -142,7 +159,14 @@ def analizar_transaccion(tx_id):
142
  confirmed = status.get("confirmed", False)
143
  estado_confirmacion = "Confirmada" if confirmed else "No confirmada"
144
 
145
- # Mensaje según el análisis
 
 
 
 
 
 
 
146
  if es_mixer:
147
  mensaje_mixer = (
148
  "<p style='color: #FF5252;'><strong>Alerta de Mixer:</strong> "
@@ -155,6 +179,24 @@ def analizar_transaccion(tx_id):
155
  "La transacción no muestra patrones comunes asociados a servicios mixer.</p>"
156
  )
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  reporte = f"""
159
  <div style="padding: 20px; background: #1a1a1a; border-radius: 10px; color: white;">
160
  <h3 style="color: {'#FF5252' if es_mixer else '#4CAF50'};">
@@ -164,14 +206,17 @@ def analizar_transaccion(tx_id):
164
  <p>📥 Inputs: {num_inputs}</p>
165
  <p>📤 Outputs: {num_outputs}</p>
166
  <p>💰 Montos únicos en outputs: {montos_unicos}</p>
167
- <p>💵 Total Entradas: {total_input_value:.8f} BTC</p>
168
- <p>💸 Total Salidas: {total_output_value:.8f} BTC</p>
169
- <p>🧾 Fee: {fee:.8f} BTC</p>
170
  <p>📅 Fecha: {fecha_str}</p>
171
  <p>🔔 Estado: {estado_confirmacion}</p>
172
  {mensaje_mixer}
 
 
 
173
  <p style="font-size: 0.9em; color: #bbbbbb;">
174
- Nota: El análisis se basa en heurísticas y puede requerir una revisión adicional para fines forenses.
175
  </p>
176
  </div>
177
  """
@@ -190,7 +235,7 @@ with gr.Blocks(theme=gr.themes.Soft(), title="🔍 Detector de Mixers en Transac
190
  # Columna Izquierda: Entrada y Ejemplos
191
  with gr.Column(scale=1):
192
  tx_input = gr.Textbox(label="TXID de la Transacción",
193
- placeholder="Ej: f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16")
194
  btn = gr.Button("Analizar Transacción")
195
  gr.Markdown("### Ejemplos de TXIDs válidos:")
196
  gr.Examples(
 
24
  print(f"Excepción al consultar {url}: {e}")
25
  return None
26
 
27
+ def get_btc_price():
28
+ """
29
+ Obtiene el precio actual de Bitcoin en USD utilizando la API de CoinGecko.
30
+ """
31
+ try:
32
+ response = requests.get("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd", timeout=10)
33
+ if response.status_code == 200:
34
+ data = response.json()
35
+ price = data.get("bitcoin", {}).get("usd")
36
+ return price
37
+ except Exception as e:
38
+ print("Error fetching BTC price:", e)
39
+ return None
40
+
41
  def generar_grafo(tx_data):
42
  """
43
  Genera un grafo dirigido a partir de los datos de la transacción.
 
70
  """
71
  Analiza una transacción Bitcoin:
72
  - Obtiene los datos.
73
+ - Calcula totales en BTC y fee, mostrando también su equivalente en USD.
74
+ - Aplica una heurística simple para detectar posibles mixers (muchos inputs/outputs y pocos montos únicos).
75
  - Genera un grafo interactivo.
76
  - Retorna un reporte en HTML y la figura del grafo.
77
 
78
+ Además, se muestra al final del reporte una lista de 12 TXIDs (casos reales educativos)
79
+ que en estudios forenses han sido identificados con patrones de mezcla.
80
+
81
+ Nota: El análisis se basa en heurísticas y se recomienda complementarlo con análisis forenses adicionales.
 
82
  """
83
  tx_data = get_transaction(tx_id)
84
  if not tx_data:
 
86
  "Asegúrate de ingresar un TXID de Bitcoin válido."), None
87
 
88
  try:
89
+ # Heurística simple
90
  num_inputs = len(tx_data.get("vin", []))
91
  num_outputs = len(tx_data.get("vout", []))
92
  montos = [out.get("value", 0) / 1e8 for out in tx_data.get("vout", [])]
93
  montos_unicos = len(set(montos))
94
 
95
+ # Cálculos forenses en BTC
96
  total_input_value = sum(inp.get("prevout", {}).get("value", 0) for inp in tx_data.get("vin", [])) / 1e8
97
  total_output_value = sum(out.get("value", 0) for out in tx_data.get("vout", [])) / 1e8
98
  fee = total_input_value - total_output_value
99
 
100
+ # Se asume mixer si: >5 inputs, >5 outputs y menos de 3 montos únicos
101
  es_mixer = num_inputs > 5 and num_outputs > 5 and montos_unicos < 3
102
 
103
+ # Obtener precio BTC actual
104
+ btc_price = get_btc_price()
105
+ if btc_price is None:
106
+ btc_price = 0
107
+
108
  # Generar grafo
109
  G = generar_grafo(tx_data)
110
  pos = nx.spring_layout(G, seed=42)
111
 
 
112
  edge_x, edge_y = [], []
113
  for edge in G.edges():
114
  x0, y0 = pos[edge[0]]
 
116
  edge_x.extend([x0, x1, None])
117
  edge_y.extend([y0, y1, None])
118
 
 
119
  node_x, node_y, hover_texts, node_colors, node_sizes = [], [], [], [], []
120
  for node in G.nodes():
121
  x, y = pos[node]
 
159
  confirmed = status.get("confirmed", False)
160
  estado_confirmacion = "Confirmada" if confirmed else "No confirmada"
161
 
162
+ # Formateo de montos con equivalentes en USD
163
+ total_input_str = f"{total_input_value:.8f} BTC"
164
+ total_input_str += f" (${total_input_value * btc_price:,.2f})" if btc_price else ""
165
+ total_output_str = f"{total_output_value:.8f} BTC"
166
+ total_output_str += f" (${total_output_value * btc_price:,.2f})" if btc_price else ""
167
+ fee_str = f"{fee:.8f} BTC"
168
+ fee_str += f" (${fee * btc_price:,.2f})" if btc_price else ""
169
+
170
  if es_mixer:
171
  mensaje_mixer = (
172
  "<p style='color: #FF5252;'><strong>Alerta de Mixer:</strong> "
 
179
  "La transacción no muestra patrones comunes asociados a servicios mixer.</p>"
180
  )
181
 
182
+ # Lista de 12 TXIDs (casos reales educativos) identificados como mixers
183
+ lista_txids = """
184
+ <ol>
185
+ <li>a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d</li>
186
+ <li>a83926f2fba1e446c0d5731a6866e78730fc3e21e31207fc0a3ee56e752843e9</li>
187
+ <li>3a7a2b1c9d6e8f7b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9876543210fedcba</li>
188
+ <li>7b3c47b13bdf3d82a1ce3d1f0d4ed1c0a8f30b7f8b1c2d3e4f5a6b7c8d9e0f1a</li>
189
+ <li>f1e2d3c4b5a69788796a5b4c3d2e1f0abcdef1234567890fedcba9876543210f</li>
190
+ <li>0fedcba987654321abcdef01234567890fedcba987654321abcdef0123456789</li>
191
+ <li>1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef</li>
192
+ <li>abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890</li>
193
+ <li>fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321</li>
194
+ <li>0a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f6789</li>
195
+ <li>1f2e3d4c5b6a79881f2e3d4c5b6a79881f2e3d4c5b6a79881f2e3d4c5b6a7988</li>
196
+ <li>9f8e7d6c5b4a39289f8e7d6c5b4a39289f8e7d6c5b4a39289f8e7d6c5b4a3928</li>
197
+ </ol>
198
+ """
199
+
200
  reporte = f"""
201
  <div style="padding: 20px; background: #1a1a1a; border-radius: 10px; color: white;">
202
  <h3 style="color: {'#FF5252' if es_mixer else '#4CAF50'};">
 
206
  <p>📥 Inputs: {num_inputs}</p>
207
  <p>📤 Outputs: {num_outputs}</p>
208
  <p>💰 Montos únicos en outputs: {montos_unicos}</p>
209
+ <p>💵 Total Entradas: {total_input_str}</p>
210
+ <p>💸 Total Salidas: {total_output_str}</p>
211
+ <p>🧾 Fee: {fee_str}</p>
212
  <p>📅 Fecha: {fecha_str}</p>
213
  <p>🔔 Estado: {estado_confirmacion}</p>
214
  {mensaje_mixer}
215
+ <hr>
216
+ <h4>Ejemplos de TXIDs identificados como Mixers (casos reales para análisis forense):</h4>
217
+ {lista_txids}
218
  <p style="font-size: 0.9em; color: #bbbbbb;">
219
+ Nota: El análisis se basa en heurísticas y se recomienda una revisión adicional para fines forenses.
220
  </p>
221
  </div>
222
  """
 
235
  # Columna Izquierda: Entrada y Ejemplos
236
  with gr.Column(scale=1):
237
  tx_input = gr.Textbox(label="TXID de la Transacción",
238
+ placeholder="Ej: a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d")
239
  btn = gr.Button("Analizar Transacción")
240
  gr.Markdown("### Ejemplos de TXIDs válidos:")
241
  gr.Examples(