Comprobaba el tamaño con 'stat -c%s'. macOS trae la versión BSD, que usa -f%z, así que el comando fallaba, caía en el '|| echo 0' y el script daba por fallida una descarga correcta: 'FALLÓ (HTTP 200, 0 bytes)'. El 200 decía que había ido bien; lo roto era la comprobación. Ahora usa wc -c, igual en los dos sistemas, y comprueba que el archivo exista. La cabecera ya no dice 'ejecutar en el nodo': si el dashboard puede vivir en una carpeta cualquiera, su instalador de fuentes también.
972 lines
55 KiB
Markdown
972 lines
55 KiB
Markdown
# Changelog
|
||
|
||
Todos los cambios notables de Txoko Node Dashboard se documentan aquí.
|
||
|
||
El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.0.0/)
|
||
y el versionado sigue [Versionado Semántico](https://semver.org/lang/es/):
|
||
`MAYOR.MENOR.PARCHE`.
|
||
|
||
- **MAYOR** — cambios grandes que cambian cómo se usa la herramienta
|
||
- **MENOR** — características nuevas que no rompen lo anterior
|
||
- **PARCHE** — arreglos de errores
|
||
|
||
---
|
||
## [1.18.2] — 2026-08-08
|
||
### Corregido
|
||
- **`instalar-fuentes.sh` daba por fallidas descargas que habían ido bien, en
|
||
macOS.** Comprobaba el tamaño con `stat -c%s`, que es sintaxis GNU. macOS
|
||
trae la versión BSD de `stat`, que usa `-f%z`, así que el comando fallaba,
|
||
caía en el `|| echo 0` y el script informaba **«FALLÓ (HTTP 200, 0 bytes)»**.
|
||
El `HTTP 200` decía que la descarga había funcionado; lo roto era la
|
||
comprobación.
|
||
- El mensaje además engañaba: se lee como un problema de red o del servidor
|
||
de fuentes, y era una incompatibilidad de shell entre dos sistemas.
|
||
- Ahora usa `wc -c`, que se comporta igual en Linux y en macOS, y comprueba
|
||
antes que el archivo exista.
|
||
- La cabecera del script decía «Ejecutar EN EL NODO». Ya no: si el dashboard
|
||
puede vivir en una carpeta cualquiera, su instalador de fuentes tiene que
|
||
funcionar ahí también.
|
||
|
||
---
|
||
## [1.18.1] — 2026-08-08
|
||
### Corregido
|
||
- **El aviso del xpub decía una media verdad.** El panel de wallet watch-only
|
||
decía «Txoko deriva tus direcciones en local — el xpub no sale del
|
||
navegador». Es cierto, y por eso mismo engaña: el xpub no sale, pero **las
|
||
direcciones derivadas sí**, una consulta a la API del nodo por cada una. Quien
|
||
opere ese nodo puede ver en sus registros el monedero completo que estás
|
||
analizando: cuántas direcciones tienes, cuáles, su historial y tu saldo.
|
||
- Con el nodo propio no hay problema, es tu información en tu máquina. Con el
|
||
nodo de otra persona —aunque sea de confianza— le estás enseñando tus
|
||
finanzas enteras.
|
||
- El aviso lo dice ahora, y distingue los dos casos. En una herramienta que
|
||
existe para no afirmar de más, ese era el peor sitio posible para tener una
|
||
frase técnicamente cierta que se lee como una garantía que no es.
|
||
|
||
### Añadido
|
||
- `PRUEBALA.md` — guía para quien recibe la aplicación para probarla: el aviso
|
||
del xpub por delante de todo, qué merece la pena intentar romper, qué no
|
||
hacer con el peritaje, y qué tipo de fallo interesa que reporte.
|
||
|
||
---
|
||
## [1.18.0] — 2026-08-08
|
||
### Añadido
|
||
- **La comisión como huella.** El sat/vB que eliges dice algo del software y de
|
||
quien lo maneja, y el dato estaba en cada transacción sin que nadie lo
|
||
mirase. Se detectan dos firmas: una comisión **absoluta** redonda (10.000
|
||
sats clavados — un estimador nunca da eso, calcula tarifa por tamaño y
|
||
devuelve números feos) y una **tarifa** prácticamente entera (20,00 sat/vB,
|
||
que sale de teclear un número redondo en la casilla).
|
||
- Informativo y sin penalización, por el mismo motivo que se decidió con RBF:
|
||
la señal es débil, y bajar la nota por ella empujaría a elegir comisiones
|
||
peores solo para disimular. Mal negocio.
|
||
- **El cambio ya se identifica en transacciones de más de dos salidas, cuando
|
||
el caso es inequívoco.** Antes la detección se plantaba en seco por encima de
|
||
dos salidas. Estaba bien argumentado, pero se tiraba información sólida: si
|
||
de N salidas **exactamente una** comparte tipo de script con las entradas, no
|
||
hay nada que adivinar — y con más salidas la coincidencia por azar es aún más
|
||
improbable que con dos. También cuenta el caso de una salida que vuelve a una
|
||
dirección ya gastada. Se marca POSIBLE y no PROBABLE: es una sola señal, y
|
||
hay monederos que devuelven el cambio a un tipo distinto del que gastan.
|
||
- **Aviso de autoenvíos que ataron direcciones**, en el informe de wallet. Es
|
||
una comprobación que solo cabe ahí: para saber que *todas* las salidas son
|
||
tuyas hace falta conocer tu wallet entero, cosa que el analizador de una
|
||
transacción suelta no puede saber.
|
||
- Detecta las transacciones donde entradas y salidas son todas tuyas, y avisa
|
||
solo cuando hubo daño real: más de una dirección de entrada. Mover una
|
||
dirección a otra no vincula nada nuevo y no merece alarma.
|
||
- Es un gesto que suele hacerse creyendo que despista y hace lo contrario: no
|
||
hubo ningún pago, pero al firmar varias direcciones a la vez quedaron
|
||
enlazadas en público. Se paga una comisión por empeorar la propia
|
||
privacidad.
|
||
|
||
Con esto quedan cerradas las cinco heurísticas que el repaso del 2026-08-08
|
||
señaló como ausentes.
|
||
|
||
---
|
||
## [1.17.0] — 2026-08-08
|
||
### Cambiado
|
||
- **La nota de privacidad ya solo mide lo que dependía de ti.** Hasta ahora
|
||
mezclaba dos cosas distintas en un número: los errores propios (reutilizar
|
||
direcciones, consolidar, dejar el cambio a la vista) y la exposición que
|
||
provoca un tercero (que te paguen desde un lote, recibir polvo, que la otra
|
||
parte use un tipo de dirección distinto). La consecuencia era absurda:
|
||
**alguien impecable que cobró de un exchange sacaba peor nota que un
|
||
descuidado que cobró de un particular**, y no había nada que pudiera hacer
|
||
para mejorarla. Recibir de un lote costaba 23 puntos y estaba marcado, con
|
||
razón, como "no corregible".
|
||
- La nota se calcula ahora solo con los checks sobre los que tienes decisión.
|
||
Denominador 142.
|
||
- Lo demás pasa a un bloque propio, **«lo que hicieron otros»**, con su
|
||
propio nivel de exposición (ninguna / moderada / alta). Se informa, se
|
||
explica, y no baja una nota que no podrías subir.
|
||
- Una nota que no puedes mejorar no es una evaluación, es un reproche. Y una
|
||
herramienta que reprocha lo que no elegiste enseña a ignorarla.
|
||
- **Un fallo grave impide la banda ALTA aunque el número dé de sobra.** Con la
|
||
nota restringida a lo propio, una transacción con el cambio identificable
|
||
—una fuga real y concreta— sacaba 93 sobre 100 y la etiqueta "privacidad
|
||
aceptable". Ahora cualquier check propio que falle con peso ≥10 impide la
|
||
banda alta. Un promedio bueno no borra un fallo concreto.
|
||
- **OP_RETURN deja de forzar banda BAJA por decreto.** Sigue penalizando, pero
|
||
el tope duro condenaba por igual a una inscripción y a un sello de tiempo de
|
||
OpenTimestamps, que es privacidad neutra. Como además OP_RETURN suele venir
|
||
del protocolo que usaste y no de tu descuido, vive ahora en la exposición
|
||
heredada.
|
||
- Los cortes de banda pasan a 85 y 55: con la nota midiendo solo lo propio, el
|
||
listón puede ser más exigente porque ya no hay dentro nada que no puedas
|
||
arreglar.
|
||
- La exportación en Markdown y en JSON incluyen ambos bloques por separado.
|
||
|
||
---
|
||
## [1.16.0] — 2026-08-08
|
||
### Corregido
|
||
- **El denominador de la nota de privacidad no cuadraba con los pesos.** La
|
||
nota se calcula como `100 - (deducciones / maxPossible) * 100`, donde
|
||
`maxPossible` es la suma del objeto `weights`. Ese objeto tenía dos
|
||
desajustes, en direcciones opuestas y ninguno intencionado:
|
||
- Incluía `rbf` (5) y `peeling` (8), que son informativos y **nunca restan**.
|
||
Trece puntos de denominador imposibles de alcanzar, que inflaban
|
||
sistemáticamente todas las notas.
|
||
- **No** incluía `input_linkage`, que resta hasta 30 desde una variable
|
||
suelta. Las deducciones podían superar al denominador en 17 puntos y dar
|
||
una nota negativa, tapada solo por el `Math.max(0, …)`.
|
||
- Ahora todo lo que puede restar está en `weights` y nada más lo está. Los
|
||
cortes de banda pasan de 75/45 a 79/54, que son los valores que hacen que
|
||
una transacción reciba **exactamente la misma banda que antes** con el
|
||
denominador corregido. Queda anotado en el código que normalizar por la
|
||
suma de los pesos obliga a recalibrar cada vez que se añade un check —
|
||
porque cada check nuevo hace parecer menos graves a los anteriores.
|
||
- El texto explicativo bajo la nota derivaba la banda por su cuenta con los
|
||
cortes antiguos escritos a mano. Ahora usa la banda ya calculada.
|
||
- **"Cifra redonda" estaba mal definido.** La condición era
|
||
`v % 1000000 === 0 || v % 100000 === 0 || v % 10000000 === 0`, donde la
|
||
primera y la tercera sobran: todo múltiplo de un millón lo es de cien mil.
|
||
Equivalía a "múltiplo de 0,001 BTC", así que pagos tan redondos como 10.000
|
||
o 50.000 sats —de lo más común con las comisiones de hoy— no se veían. La
|
||
heurística estaba infrautilizada, no equivocada.
|
||
- Ahora se mide por ceros finales y la señal se gradúa: pagar 0,5 BTC
|
||
clavados delata más que pagar 0,0123, y la penalización lo refleja.
|
||
- El didáctico dice ahora el punto ciego: si pagas una cantidad redonda **en
|
||
euros**, en BTC sale un número con todos sus decimales y esta heurística no
|
||
ve nada.
|
||
|
||
### Añadido
|
||
- **Nuevo aviso: se gasta polvo junto a otras monedas.** El check de polvo
|
||
advertía de que una salida diminuta "quedará vinculada con las demás si se
|
||
gasta junto a ellas" — y la aplicación nunca miraba si eso estaba ocurriendo
|
||
delante de ella, pese a tener el dato en la mano. Ahora lo mira: si entre las
|
||
entradas hay una moneda de tamaño polvo gastada junto a otras de importe
|
||
normal, el ataque ya no es una hipótesis, se acaba de consumar, y se dice con
|
||
CERTEZA. No aplica en CoinJoin ni cuando solo se gasta polvo, que no revela
|
||
vinculación nueva.
|
||
- **La consolidación pura ya no pasa desapercibida.** Una transacción de N
|
||
entradas a **una sola salida** —el acto que más privacidad destruye de una
|
||
vez— no disparaba el check de inputs innecesarios: su condición exige que una
|
||
entrada cubra el pago *y sobre*, y en una consolidación la salida vale casi
|
||
lo mismo que la suma de entradas. Comprobado con 20 entradas a 1 salida.
|
||
Ahora se nombra como lo que es, con certeza. No suma penalización propia a
|
||
propósito: la vinculación ya la cobra `input_linkage`, y cobrarla dos veces
|
||
sería repetir el error que este mismo repaso acaba de corregir.
|
||
- `tests/test6.js` — el analizador completo contra transacciones construidas a
|
||
mano: coherencia de la nota, los dos checks nuevos y la guardia de CoinJoin.
|
||
- `tests/test5.js` gana la comprobación de los niveles de redondez.
|
||
- `tests/README.md` explica ahora las dos familias de prueba y deja escrito que
|
||
las heurísticas se comprueban contra transacciones fabricadas, no contra la
|
||
cadena real: eso demuestra que la lógica hace lo que dice, pero no cuántas
|
||
veces acierta ahí fuera.
|
||
|
||
---
|
||
## [1.15.0] — 2026-08-08
|
||
### Corregido
|
||
- **La detección del cambio contaba una misma señal dos veces.** La "señal A"
|
||
(solo una salida comparte tipo de script con las entradas) y la "señal E"
|
||
(el tipo de output difiere del de las entradas) eran la misma condición
|
||
escrita dos veces —el mismo `filter`, la misma comparación, el mismo `=== 1`—
|
||
y cada una incrementaba el contador. Como el umbral para declarar el cambio
|
||
identificable es de dos señales, **ese único hecho bastaba por sí solo**.
|
||
- Consecuencia práctica: pagar desde una dirección bech32 a una taproot —de
|
||
lo más común que hay— salía marcado como "cambio identificable" con certeza
|
||
PROBABLE, sin ninguna otra evidencia.
|
||
- Y el informe mostraba las dos señales juntas, que se contradicen entre sí:
|
||
"tipo de script idéntico a inputs" y "tipo de output distinto al de inputs".
|
||
El mismo hecho descrito con palabras opuestas.
|
||
- Ahora es una sola señal. Esa transacción pasa a una señal y a **no
|
||
identificable**, que es lo correcto. Cuando queda una sola señal el check
|
||
lo dice en vez de callarse: existe, no basta, y conviene que se sepa que
|
||
otro analista menos escrupuloso la daría por buena ella sola.
|
||
- **Se podía señalar el pago como si fuera el cambio.** Cuando ninguna señal
|
||
fuerte apuntaba a un output concreto, el índice caía en `indexOf(smaller)`:
|
||
se asumía que el cambio es siempre la salida menor. Es falso en el caso más
|
||
común de todos —pagar poco desde una moneda grande—, donde el cambio es la
|
||
salida *mayor*.
|
||
- Comprobado con un pago de 0,001 BTC desde 1 BTC: la app decía en el detalle
|
||
"el output redondo es el pago" y acto seguido señalaba ese mismo output
|
||
como cambio. La información para acertar estaba delante y se descartaba.
|
||
- Esto no se quedaba en el analizador: el motor de peritaje usa ese índice
|
||
para detectar cadenas de peeling y para redactar el informe. Un cambio mal
|
||
identificado significa presentar como rastro del actor **la dirección del
|
||
destinatario del pago**, una persona ajena.
|
||
- Ahora cada señal apunta a un output o reconoce que no puede. El orden de
|
||
resolución es: reutilización de dirección de entrada → tipo de script →
|
||
la salida NO redonda → posición. Si ninguna apunta, el índice queda vacío
|
||
y se dice que hay señales pero no cuál — preferible a señalar mal.
|
||
|
||
### Añadido
|
||
- `tests/test5.js` — seis casos de detección de cambio con el pago y el cambio
|
||
conocidos de antemano, incluidos los dos fallos anteriores como regresión.
|
||
|
||
---
|
||
## [1.14.0] — 2026-08-08
|
||
### Añadido
|
||
- **Mempool y Bloques se fusionan en una sola pestaña, «Cadena».** Eran el
|
||
mismo eje partido por la mitad: los "bloques proyectados" vivían en Mempool y
|
||
los confirmados en Bloques, cuando es una línea temporal con el ahora en el
|
||
corte. Ahora se lee de arriba abajo: comisiones, lo que está por minar, un
|
||
separador **AHORA**, y los bloques ya minados.
|
||
- Nuevo: **«si pagas X sat/vB, ¿cuándo entra?»**. Ni la vista de mempool ni la
|
||
de bloques respondían la pregunta que de verdad se hace quien mira las
|
||
comisiones — daban los datos por separado y dejaban el cálculo al ojo.
|
||
Escribes una tarifa y te dice en qué bloque proyectado caería, marcándolo
|
||
en la lista. Con dos avisos escritos en la propia interfaz: el cálculo se
|
||
hace sobre la mempool de este momento, y diez minutos es la media entre
|
||
bloques, no una promesa.
|
||
- El detalle de un bloque se abría al final de la lista, a quince filas del
|
||
bloque pulsado, así que parecía que el clic no hacía nada. Ahora la vista
|
||
se desplaza hasta él.
|
||
- **Aviso de polvo recibido** en el informe de wallet. El analizador ya sabía
|
||
reconocer el patrón al mirar una transacción suelta, pero eso no sirve si no
|
||
te avisa cuando te pasa a ti. Recorre las salidas de importe ínfimo hacia
|
||
direcciones propias y distingue las que siguen sin gastar —donde el consejo
|
||
todavía sirve— de las ya gastadas, donde la vinculación ya está hecha.
|
||
El consejo es el contrario del instinto: **no hagas nada**, déjalas quietas y
|
||
congélalas en el monedero si puedes. El daño solo ocurre al mezclarlas.
|
||
- **Enlaces «ver en Mempool»** en nueve puntos: transacción analizada, detalle
|
||
de bloque y sus transacciones, dirección del explorador, historial del informe
|
||
de wallet, direcciones atribuidas y fondos localizados del peritaje, y en el
|
||
rastro de procedencia la dirección de cada entrada y su transacción de origen.
|
||
Se construyen **siempre** desde la URL configurada; sin nodo, no hay enlace.
|
||
|
||
### Corregido
|
||
- **El rastro de procedencia enlazaba a mempool.space público** cuando no había
|
||
nodo configurado (`base ? … : "https://mempool.space/tx/…"`). Un clic ahí le
|
||
dice a un tercero qué transacción estás investigando, que es exactamente lo
|
||
contrario de para lo que existe esta herramienta. Sin nodo ya no hay enlace:
|
||
se muestra el txid en texto plano. Auditado que no queda ningún enlace
|
||
saliente en toda la aplicación.
|
||
- La fee mediana del detalle de bloque se mostraba con dieciséis decimales.
|
||
|
||
### Nota de diseño
|
||
- Se descartó hacer persistentes las etiquetas BIP-329. Vinculan direcciones con
|
||
descripciones en lenguaje natural ("ahorro", "pago a…") y guardarlas dejaría
|
||
en disco justo el mapa que un atacante querría. Que vivan solo en memoria no
|
||
es una carencia, es la misma decisión que ya se aplica a la URL del nodo.
|
||
|
||
---
|
||
## [1.13.0] — 2026-08-08
|
||
### Cambiado
|
||
- **El checklist decía que un swap alto era "normal en nodos con índice
|
||
completo".** No lo es, y decirlo hace que se ignore durante semanas un
|
||
síntoma con causa y con arreglo. El caso que lo cambió: swap al 99% con 18 GB
|
||
de RAM libre, porque Fulcrum tenía `db_mem` a 16 GB — más de lo que cabe en
|
||
la máquina junto al resto de servicios. Con el swap lleno el sistema se queda
|
||
sin margen y, ante un pico, el OOM killer elige él a quién mata.
|
||
- El umbral baja del 95% al 50%, que es cuando conviene mirarlo y no cuando
|
||
ya es tarde, y pasa a estado de error por encima del 80%.
|
||
- Si el swap sube **teniendo RAM libre**, el aviso lo señala: es la firma de
|
||
un servicio con más memoria reservada de la que cabe, no de presión real.
|
||
- Apunta al sospechoso habitual (`db_mem` de Fulcrum, que se sube para
|
||
acelerar la sincronización inicial y es fácil olvidarse de bajar) e incluye
|
||
el comando para ver qué proceso ocupa el swap.
|
||
- Misma corrección en la explicación de SWAP de los DOCS.
|
||
|
||
### Añadido
|
||
- **Nueva comprobación: direcciones que quedan vinculadas entre sí.** Era el
|
||
hueco de fondo del analizador: medía la privacidad desde el lado de quien
|
||
*construye* la transacción (¿se ve mi cambio?, ¿pago cifras redondas?) y no
|
||
desde el lado de los dueños de las monedas gastadas, que es donde el daño es
|
||
irreversible. Gastar N direcciones distintas a la vez las enlaza para siempre
|
||
por CIOH, y eso no se decía en ningún sitio. El peso sube con el número de
|
||
direcciones y no aplica en CoinJoin, donde romper esa vinculación es el
|
||
objetivo.
|
||
|
||
### Corregido
|
||
- **El explorador mostraba saldo cero en direcciones con mucho historial.** El
|
||
backend de Mempool sobre Fulcrum devuelve los importes a cero cuando la
|
||
dirección tiene muchas transacciones, aunque el contador venga bien — y la app
|
||
los presentaba como ciertos. Una dirección con 492 transacciones aparecía con
|
||
"Balance: 0,00000000 BTC". Ahora se detecta la inconsistencia (tener
|
||
transacciones y no haber recibido nada es imposible en la cadena), los
|
||
importes se muestran como "no disponible" y se explica por qué. El contador de
|
||
transacciones y el historial, que sí son correctos, se mantienen.
|
||
- Mismo arreglo en el perfil de dirección del peritaje: antes concluía "no es
|
||
hot wallet" cuando en realidad no había podido evaluar la señal de flujo.
|
||
- **El fingerprinting atribuía transacciones a Electrum por AUSENCIA de rasgos**
|
||
(locktime 0, sin RBF, sequence por defecto). Es justo al revés: Electrum
|
||
moderno pone locktime a la altura actual y señaliza RBF. Lo que esas señales
|
||
describen no es un monedero concreto, sino software que no configura nada.
|
||
Ahora Electrum exige señales positivas y aparece un resultado nuevo, **"sin
|
||
rasgos distintivos"**, que además no penaliza: no dejar huella es lo contrario
|
||
de tener una huella identificable.
|
||
|
||
---
|
||
## [1.12.1] — 2026-07-27
|
||
### Corregido
|
||
- **El monitor mostraba una CPU por proceso que engañaba.** La columna `%CPU`
|
||
de `ps aux` no es el consumo actual sino la **media desde que el proceso
|
||
arrancó** (tiempo de CPU dividido por tiempo de vida). Un proceso que trabajó
|
||
mucho hace tres días seguía apareciendo alto para siempre, y se leía como si
|
||
estuviera saturando la máquina. Se detectó porque los porcentajes por proceso
|
||
no cambiaban NUNCA entre lecturas mientras la CPU global sí variaba.
|
||
- Ahora se mide de verdad: dos lecturas de `/proc/PID/stat` separadas 500 ms,
|
||
el mismo método que ya usaba `getCpuUsage` para el total. Las dos esperas
|
||
corren en paralelo dentro del mismo `Promise.all`, así que no cuesta tiempo.
|
||
- La interfaz muestra el **porcentaje sobre el total de la máquina**, que es
|
||
lo que suele querer saberse, con el porcentaje de un núcleo y la media de
|
||
`ps` en el tooltip para poder comparar con `top`.
|
||
- Comprobado con carga artificial: el proceso ocupado marcaba 100% de un
|
||
núcleo (25% de un sistema de 4) mientras `ps` daba 152%, imposible para un
|
||
proceso de un solo hilo.
|
||
|
||
### Cambiado
|
||
- El UTXO Map pedía las transacciones de contexto con `fetchWithTimeout`
|
||
directo, saltándose la caché añadida en la 1.11.0. Era el único punto que no
|
||
la aprovechaba. Ahora pasa por `get()`, así que esas transacciones —ya
|
||
confirmadas, e inmutables— se guardan toda la sesión.
|
||
|
||
### Documentación
|
||
- **Reescrita la guía de instalación.** `SETUP.md` no era una guía: más de la
|
||
mitad eran los pasos personales de git y Gitea del autor ("PASO 1 — Crear el
|
||
repo en Gitea"), que se han movido a un documento privado. Ahora es una guía
|
||
real, con una comprobación tras cada paso para saber si vas bien sin
|
||
descubrirlo al final con una pantalla en blanco.
|
||
- **Corregida la configuración de nginx documentada, que estaba rota.** El
|
||
README indicaba `alias /var/www/txoko/dashboard.html` — un alias a un
|
||
ARCHIVO. Desde la 1.11.0, con las librerías en `vendor/` y rutas relativas,
|
||
eso hace que el navegador no las encuentre y la página quede en blanco sin
|
||
ningún error visible. Debe apuntar al **directorio**. Es el fallo más común
|
||
de esta instalación y ahora está avisado en tres sitios, con una comprobación
|
||
concreta (`curl` mirando el `content-type`, no solo el código 200) para
|
||
descartarlo.
|
||
- Los pasos de instalación no mencionaban las librerías del frontend, sin las
|
||
cuales la aplicación no arranca. Ya están integradas en el orden correcto.
|
||
- Eliminados del repositorio los datos personales del autor: rutas de su
|
||
máquina en la documentación y, sobre todo, su nombre de usuario del sistema
|
||
y su ruta de instalación dentro de `txoko-metrics.service`, que llevaban ahí
|
||
desde el primer commit. El servicio usa ahora marcadores que hay que ajustar
|
||
antes de instalarlo.
|
||
- README: estructura del repositorio actualizada (faltaban `tests/`,
|
||
`instalar-fuentes.sh` y `FIXTURES.md`) y sección de instalación reducida a un
|
||
resumen que remite a SETUP.md, para que haya una sola fuente de verdad.
|
||
- SETUP.md incluye ahora tabla de problemas frecuentes y cómo actualizar.
|
||
|
||
---
|
||
## [1.12.0] — 2026-07-27
|
||
### Añadido
|
||
- **Auditoría de PSBT: revisar una transacción ANTES de firmarla.** Hasta ahora
|
||
toda la app era diagnóstico *a posteriori* — te contaba con detalle lo que ya
|
||
había pasado y no podías cambiar. Esta es la primera pieza que llega a tiempo.
|
||
Sustituye al antiguo "validador", que solo comprobaba que el archivo empezara
|
||
por `psbt` y decía su tamaño.
|
||
- **Parser completo de BIP174 escrito desde cero**, sin librerías, como el
|
||
resto del proyecto. Validado contra los cinco vectores inválidos que publica
|
||
el propio estándar: los rechaza los cinco, cada uno con un mensaje que
|
||
explica en castellano qué está mal (truncada, sin salidas, ya firmada,
|
||
sin transacción interna, con claves repetidas).
|
||
- **Todo el análisis es offline.** Una PSBT bien formada ya lleva dentro los
|
||
importes y scripts de sus entradas, así que no hace falta consultar el nodo:
|
||
funciona con el nodo sincronizando, y el nodo ni se entera.
|
||
- Acepta base64, hexadecimal o abrir el archivo `.psbt` directamente.
|
||
- **Aviso crítico sobre xpubs incrustados.** Las PSBT suelen llevar dentro las
|
||
claves públicas maestras de la cartera, y están hechas para compartirse — se
|
||
mandan por correo o chat para que el resto firme. Quien reciba el archivo
|
||
puede derivar todas las direcciones, presentes y futuras, y ver el saldo y
|
||
el historial completos. No puede gastar, pero lo ve todo. Ningún wallet
|
||
avisa de esto.
|
||
- Detecta además: cambio identificable por tipo de dirección, importes
|
||
redondos que delatan cuál es el pago, envíos a la propia cartera, carteras
|
||
multifirma (con su M-de-N) y PSBTs incompletas sin los importes de entrada.
|
||
- Cada aviso mantiene el formato del resto de la app — Hecho, Consecuencia y
|
||
**Qué puedes hacer** —, que aquí cobra sentido literal: todavía estás a
|
||
tiempo de cambiar la transacción.
|
||
|
||
### Corregido
|
||
- **Revisión de la criptografía watch-only.** Se verificó la derivación completa
|
||
contra los vectores oficiales de los estándares: RIPEMD-160 (los seis del
|
||
estándar, incluido el de un millón de caracteres), secp256k1, BIP32 (vectores
|
||
1 y 2) y bech32/BIP173. **La matemática es correcta y no se tocó.** Los fallos
|
||
estaban en la validación de la entrada:
|
||
- **No se comprobaba la suma de verificación del xpub.** Un carácter mal
|
||
copiado se aceptaba sin protestar y generaba 200 direcciones ajenas: el
|
||
usuario habría visto su cartera "sin actividad" y se habría quedado
|
||
tranquilo. Falso negativo silencioso, el mismo patrón que el resto de fallos
|
||
de esta jornada. Nuevo `decodeBase58Check`.
|
||
- **No se validaba la longitud** del material decodificado (deben ser 78
|
||
bytes) ni que la clave pública fuese comprimida (0x02/0x03).
|
||
- **`tpub` se trataba como mainnet.** La red se detectaba mirando si el texto
|
||
empezaba por "tb", "u" o "v" — y un `tpub`, el formato más común de testnet,
|
||
empieza por "t" pero no por "tb". Generaba direcciones `bc1…` a partir de
|
||
claves de testnet. Ahora se detecta por bytes de versión, que son
|
||
inequívocos, con las diez variantes (x/y/z/Y/Z y t/u/v/U/V).
|
||
- **`deriveChildPubkey` aceptaba índices endurecidos**, matemáticamente
|
||
imposibles desde una clave pública. No era alcanzable desde la interfaz,
|
||
pero una función criptográfica debe defenderse sola.
|
||
- Añadida la carpeta `tests/` con las cuatro baterías y su documentación,
|
||
incluido qué **no** cubren: no sustituyen una auditoría externa.
|
||
|
||
---
|
||
## [1.11.0] — 2026-07-27
|
||
### Añadido
|
||
- **Las librerías del frontend se sirven desde tu propio nodo.** React, ReactDOM
|
||
y Babel se cargaban desde unpkg.com, y las fuentes IBM Plex desde
|
||
fonts.googleapis.com. Ninguno de los dos veía qué transacciones analizabas —
|
||
pero ambos recibían tu IP pública, la hora y el referer en cada apertura del
|
||
dashboard: sabían que usabas Txoko, cuándo y desde dónde. Ese metadato es
|
||
justo lo que esta herramienta enseña a proteger. Ahora no sale ni una
|
||
petición fuera de tu red, verificable con
|
||
`grep -E '(src|href)="https?://' dashboard.html` (no devuelve nada).
|
||
- Consecuencia práctica: **el dashboard funciona sin conexión a internet**.
|
||
- Las librerías se descargan una vez durante la instalación y se **verifican
|
||
por hash** (SHA-256 publicados en SETUP.md).
|
||
- Versiones fijadas (`react@18.3.1`, `@babel/standalone@7.23.10`) en vez de
|
||
rangos: antes el CDN decidía qué versión te entregaba y podía cambiar sin
|
||
aviso.
|
||
- `instalar-fuentes.sh` automatiza la descarga de IBM Plex (licencia OFL) y
|
||
genera el CSS. Comprueba cada archivo y aborta si algo falla.
|
||
- **Caché de respuestas del nodo con TTL y coalescencia** en `useApi`. Las
|
||
pestañas se montan y desmontan al navegar, así que ir a otra pestaña y volver
|
||
repetía todas las peticiones. Ahora:
|
||
- Las **transacciones confirmadas se cachean toda la sesión** — son
|
||
inmutables, no hay motivo para volver a pedirlas. Analizador, rastro de
|
||
procedencia y peritaje dejan de pedir las mismas por separado.
|
||
- Historial de direcciones 60 s, bloques 60 s, mempool y comisiones 20 s,
|
||
estado del sistema 2 s (para que el monitor siga mostrando datos vivos).
|
||
- **Coalescencia:** dos componentes que piden lo mismo a la vez generan una
|
||
sola petición al nodo, no dos. Mismo patrón que ya usaba
|
||
`system-metrics.js` en el servidor.
|
||
- Medido: repetir un rastreo forense ya explorado cuesta **cero peticiones**.
|
||
|
||
### Cambiado
|
||
- **Un hit de OFAC ya no penaliza la banda de privacidad.** Era un error
|
||
conceptual con consecuencias ideológicas: que tus monedas hayan pasado por una
|
||
dirección sancionada no revela nada más sobre ti — tu privacidad es idéntica
|
||
antes y después. Lo que cambia es la probabilidad de que un servicio regulado
|
||
te bloquee un depósito, que es otro eje: censurabilidad, no privacidad.
|
||
Restar puntos ahí equivalía a dar por buena la idea de "monedas contaminadas",
|
||
justo la que la fungibilidad de Bitcoin niega. Se sigue informando del riesgo
|
||
real, ahora como aviso de censura y con el contexto que faltaba: la lista OFAC
|
||
es una decisión política de un gobierno concreto, no una determinación
|
||
judicial, y fuera de su jurisdicción solo llega a través de intermediarios que
|
||
la aplican.
|
||
- **RBF pasa a informativo, sin penalización.** Es buena práctica —permite
|
||
desatascar una transacción sin sobrepagar de entrada— y hoy lo activan casi
|
||
todos los wallets, así que distingue poco. Restar puntos empujaba al usuario
|
||
hacia una decisión peor para esconder una señal débil.
|
||
- Checks con inferencia fuerte (`round_numbers`, `unnecessary_input`,
|
||
`output_type_mismatch`) reescritos con el patrón Hecho / Interpretación /
|
||
Consecuencia que ya usaban `dust` e `input_type_mixing`. Antes afirmaban de
|
||
más ("un analista puede identificarlo sin ambigüedad"); ahora explican cuándo
|
||
la señal falla y por qué son PROBABLE y no CERTEZA.
|
||
- `timing`: el texto didáctico describía patrones entre varias transacciones
|
||
cuando el check solo ve una. Además se aclara que la hora mostrada es la del
|
||
bloque, no la de la firma — entre ambas puede haber horas de mempool.
|
||
|
||
### Documentación
|
||
- README: nueva sección **"Sí, esto es chain analysis"**. El peritaje usa las
|
||
mismas técnicas que las empresas de vigilancia de cadena y negarlo restaba
|
||
credibilidad ante quien lee el código. Se explica qué cambia —quién lo
|
||
ejecuta, sobre qué, dónde se detiene, quién se queda el informe— y el motivo
|
||
de fondo: la misma herramienta que sigue el rastro de un ladrón demuestra lo
|
||
fácil que es seguir el tuyo.
|
||
- El informe forense advierte del coste de denunciar: entregarlo vincula tu
|
||
identidad legal con esas direcciones de forma permanente, ante una autoridad
|
||
que puede compartir el expediente con empresas de análisis. Antes se
|
||
recomendaba denunciar sin mencionar el precio.
|
||
- Aviso junto a los botones de exportación: el archivo lleva tus direcciones y
|
||
tu declaración dentro. Nada ha salido de tu red hasta ese punto; a partir de
|
||
ahí depende de quien lo descarga.
|
||
- Corregido "un único archivo HTML autocontenido" (aparecía cuatro veces): desde
|
||
la 1.11.0 las librerías viven en `vendor/`. Se sustituye por una descripción
|
||
exacta que además dice más — lo que lees en el archivo es lo que se ejecuta,
|
||
no hay versión compilada que auditar por separado.
|
||
|
||
### Corregido
|
||
- **El informe ya no atribuye al actor direcciones del otro lado de un
|
||
CoinJoin.** Detectado probando un rastro que atraviesa un Whirlpool real: el
|
||
informe declaraba *"el rastro se rompe en un CoinJoin — no se puede atribuir
|
||
con honestidad más allá de este punto"* y, tres líneas más abajo, listaba
|
||
**10 direcciones atribuidas al actor**, cinco de ellas salidas de esa misma
|
||
mezcla. Eran direcciones de otros participantes, señaladas en un documento
|
||
pensado para acompañar una denuncia.
|
||
- Los dos fundamentos que las colaban fallan precisamente ahí: **CIOH** asume
|
||
que quien gasta varios inputs juntos los controla, y un CoinJoin existe para
|
||
romper esa suposición (es la excepción clásica a la heurística); y la
|
||
**huella de software** sale estable por construcción, porque todos los
|
||
participantes usan el mismo programa.
|
||
- `finalizeForensicGraph` excluye ahora las transacciones marcadas como
|
||
`mixer` del conjunto de direcciones del actor y del union-find, y la
|
||
atribución por huella salta esos nodos.
|
||
- Verificado con el mismo caso: de 10 direcciones atribuidas a 0.
|
||
- **El informe de wallet ya no da un aprobado optimista cuando faltan datos.**
|
||
Mismo patrón que el fallo del peritaje, en la pieza central del proyecto:
|
||
`scanWallet` usaba `.catch(()=>[])` en las tres consultas del escaneo, así que
|
||
una dirección que el nodo no pudo servir quedaba indistinguible de una
|
||
dirección sin actividad. Sus transacciones no se traían, no contaban para la
|
||
reutilización, no entraban en el union-find y no aparecían en el historial —
|
||
y el informe daba su valoración de salud sin mencionar que le faltaban datos.
|
||
El sesgo iba siempre hacia el optimismo: menos actividad vista es mejor nota.
|
||
- Las tres consultas usan ahora `getStrict` y registran los fallos en
|
||
`report.scanErrors`, con la dirección, la rama y en qué fase ocurrió.
|
||
- Aviso en rojo **antes** de la banda de salud (una valoración leída sin saber
|
||
que faltan datos es peor que ninguna valoración), y bloque equivalente al
|
||
principio del export Markdown.
|
||
|
||
- **Tampoco atribuye al actor las direcciones de un custodio.** Misma raíz que
|
||
el fallo anterior, descubierta probando la poda por rama con un depósito real
|
||
en Bitfinex: el cluster CIOH ya excluía las direcciones de custodio, pero la
|
||
atribución por huella de software no, así que la hot wallet del exchange
|
||
acababa listada como "dirección del actor" mientras la conclusión, dos líneas
|
||
más abajo, la identificaba correctamente como Bitfinex. La dirección donde el
|
||
ladrón deposita es del exchange, no del ladrón. Ambos criterios están ahora
|
||
alineados: ni mezclas ni custodios entran por ninguna de las dos vías.
|
||
Verificado: de 2 direcciones atribuidas a 1 (solo la rama legítima).
|
||
|
||
---
|
||
## [1.10.1] — 2026-07-27
|
||
### Añadido
|
||
- **Tope de ramificación en el peritaje (`MAX_FANOUT`, 25 salidas).** Detectado
|
||
probando el fixture de dust attack: una transacción que reparte a cientos de
|
||
direcciones hacía que el motor encolara todas sus ramas y, sobre todo, que
|
||
perfilara cada dirección de salida — 2 peticiones por dirección, así que una
|
||
tx de 143 salidas suponía ~286 peticiones al nodo antes siquiera de llegar al
|
||
frontier, y la pestaña se congelaba. Ahora, por encima del tope, la tx se
|
||
trata como reparto masivo (dust attack, lote de retiradas, airdrop): se corta
|
||
ANTES de perfilar nada, con `stopReason: "fanOut"` y su propia conclusión en
|
||
el informe, redactada como límite deliberado y no como inferencia sobre quién
|
||
controla esas direcciones. Verificado con una tx real de 143 salidas: 2
|
||
peticiones en total.
|
||
|
||
### Corregido
|
||
- **Peritaje forense: un fallo de consulta ya no se presenta como "rastro
|
||
completo".** Detectado probando contra el nodo real: cuando el backend
|
||
devolvía un 503 o agotaba el timeout, la app lo mostraba como
|
||
*"✓ Rastro completo — no quedan ramas por explorar"* y contaba esa rama como
|
||
terminal. La causa eran dos capturas silenciosas encadenadas — `get` devolvía
|
||
`mockData` ante cualquier error y `findSpendingTx` remataba con
|
||
`.catch(()=>[])` —, así que `[]` por error era indistinguible de `[]` porque
|
||
el output sigue sin gastar. En un informe pericial eso convertía una consulta
|
||
fallida en la conclusión más accionable del documento: *fondos localizados
|
||
sin gastar*.
|
||
- Nuevo `getStrict` en `useApi`: propaga el fallo en vez de disfrazarlo.
|
||
`get` no cambia, así que el resto del dashboard mantiene su comportamiento.
|
||
- El motor (`findSpendingTx`, `advanceForensicHop`, `initForensicTrace`)
|
||
distingue "no se pudo consultar" de "no hay gasto" y registra los fallos en
|
||
`trace.queryErrors`.
|
||
- El informe excluye las ramas con consulta fallida de "fondos localizados
|
||
sin gastar", las lista en su propia sección y abre las conclusiones
|
||
avisando de que el rastreo está incompleto. Incluido en los export JSON y MD.
|
||
- La UI muestra contador de consultas fallidas y sustituye el falso
|
||
"rastro completo" por un aviso con el detalle de qué falló.
|
||
- Peritaje: el error al obtener la transacción de origen ya no culpa siempre al
|
||
txid — distingue un txid inexistente de un fallo del nodo.
|
||
- Peritaje: el botón "Generar informe" explica por qué está deshabilitado en
|
||
vez de quedarse inerte sin dar motivo.
|
||
|
||
### Cambiado
|
||
- El aviso de consulta fallida ya no dice que el fallo "suele ser transitorio":
|
||
cuando se repite sobre la misma dirección no lo es, y ahora lo explica —
|
||
historial demasiado pesado para servirlo dentro del tiempo de espera, con la
|
||
indicación de comprobarla a mano antes de dar el rastro por cerrado.
|
||
|
||
### Documentación
|
||
- README: documentadas dos limitaciones reales del peritaje — las ramas que no
|
||
se pueden comprobar (y por qué cortar es deliberado, no un defecto), y que el
|
||
tope de volumen mide número de transacciones y no peso, así que una dirección
|
||
con pocas transacciones muy grandes puede agotar el tiempo de espera igual.
|
||
|
||
---
|
||
## [1.10.0] — 2026-07-16
|
||
### Añadido
|
||
- Peritaje forense: nueva pestaña que rastrea fondos robados o perdidos hacia
|
||
adelante, salto a salto, desde una transacción de origen (`txid:vout`) hasta
|
||
un punto de parada natural — custodio identificado, dilución excesiva,
|
||
CoinJoin real, o fondos aún sin gastar. Todo el rastreo corre sobre tu
|
||
propio nodo (Bitcoin Core + Fulcrum + Mempool self-hosted), como el resto
|
||
de la app: ninguna consulta sale de tu red.
|
||
- Sigue todos los outputs de cada salto (no solo el presunto cambio),
|
||
porque los fondos pueden repartirse en varias ramas; cada rama se
|
||
detiene de forma independiente.
|
||
- **A demanda, salto a salto:** el rastreo no corre solo — tú decides
|
||
cuándo se explora el siguiente salto con el botón "Seguir el rastro",
|
||
viendo en todo momento cuántas transacciones se han explorado y
|
||
cuántas ramas quedan pendientes. Mismo espíritu que el rastro de
|
||
procedencia hacia atrás (RastroProcedencia), aplicado al rastreo hacia
|
||
adelante. Puedes generar el informe con lo explorado hasta ese momento,
|
||
sin necesidad de terminar el rastreo entero.
|
||
- Tope de volumen de transacciones (`tx_count`) como cinturón de
|
||
seguridad aparte del control manual: si una dirección de salida tiene
|
||
un historial extremadamente largo (miles de transacciones — típico de
|
||
un hot wallet de exchange), el rastro se detiene ahí por precaución
|
||
aunque el heurístico de perfil no la clasifique como custodio. Evita
|
||
que una sola rama intente paginar el historial completo de una
|
||
dirección con decenas de miles de transacciones.
|
||
- Reutiliza las heurísticas existentes del analizador (CIOH, detección de
|
||
cambio estructural, huella de wallet, CoinJoin, entidades conocidas) y
|
||
añade tres nuevas: perfil de dirección (personal vs hot wallet de
|
||
exchange), cambio conductual (se gasta rápido vs queda quieto) cruzado
|
||
con la señal estructural, y comparación de huella de software entre
|
||
saltos consecutivos.
|
||
- Confirma cadenas de peeling multi-salto: el check de patrón de pago
|
||
simple ya avisaba de que una sola transacción no basta para confirmar
|
||
una cadena — este módulo es lo que cierra ese hueco, siguiendo el rastro
|
||
hacia adelante de verdad.
|
||
- Marco de tres niveles en cada afirmación del informe: HECHO (dato
|
||
on-chain verificable), INFERENCIA (conclusión de una heurística, con su
|
||
propia sub-etiqueta CERTEZA/PROBABLE/POSIBLE) y DECLARACIÓN (lo que
|
||
cuenta el afectado, en un formulario de texto libre, siempre mostrado
|
||
aparte y nunca mezclado con los hechos).
|
||
- Informe con resumen, declaración del afectado, cronología, direcciones
|
||
atribuidas con su fundamento, fondos localizados sin gastar,
|
||
conclusiones numeradas, recomendaciones (con un aviso fijo contra
|
||
estafas de "recuperación de fondos" siempre que hay declaración),
|
||
metodología y anexo de verificación. Exportable a JSON y Markdown, igual
|
||
que el informe de wallet.
|
||
- No identifica la identidad real de nadie: como mucho llega a "hot wallet
|
||
de [exchange]" o "custodio no identificado". No hace denuncias ni envíos
|
||
automáticos a terceros — el informe es un documento que decides tú a
|
||
dónde llevar.
|
||
|
||
### Cambiado
|
||
- `unionFindCluster` (vinculación por CIOH) y `guessChangeOutput` (señales de
|
||
cambio estructural) se extrajeron de `buildWalletReport` y `analyzeTx`
|
||
respectivamente a funciones compartidas, sin cambio de comportamiento, para
|
||
que el peritaje forense las reutilice salto a salto.
|
||
|
||
---
|
||
## [1.9.0] — 2026-06-12
|
||
### Añadido
|
||
- Informe de wallet completo. Tras cargar tu xpub (watch-only), el botón
|
||
"Analizar wallet" examina el conjunto de tus transacciones y produce un
|
||
informe con tres partes: salud general (ALTA/MEDIA/BAJA según reutilización
|
||
de direcciones y vinculación), clusters de vinculación detectados por la
|
||
heurística CIOH (common-input-ownership: direcciones que gastaste juntas y
|
||
que un observador asume del mismo dueño), e historial de cada transacción
|
||
con su banda de privacidad. Exportable a JSON y Markdown. Es la visión de
|
||
conjunto que ningún explorador ofrece: no analiza una transacción aislada,
|
||
sino qué puede agrupar un observador de toda tu actividad. El escaneo se
|
||
hace por lotes con pausas para no saturar el nodo, y todo el cálculo ocurre
|
||
en el navegador.
|
||
- Presentación de resultados reorganizada. El informe de privacidad ahora
|
||
abre con un resumen ("N señales a revisar · M correctas · K informativas"),
|
||
mantiene los problemas siempre visibles y agrupa las señales correctas e
|
||
informativas en secciones colapsables. Al desplegar un problema, la
|
||
explicación didáctica aparece directa, con una línea "Qué puedes hacer"
|
||
según el caso (ya está en la cadena / depende de tu wallet / puedes
|
||
evitarlo al construir la transacción).
|
||
|
||
### Cambiado
|
||
- Las recomendaciones usan un lenguaje más claro y menos técnico: "depende
|
||
del wallet" y "puedes evitarlo" en lugar de etiquetas crípticas. El peso de
|
||
cada señal en la banda se describe como alto/medio/bajo en vez de un número.
|
||
|
||
### Corregido
|
||
- El monitor del sistema (system-metrics.js) ya no usa la librería express:
|
||
ahora funciona solo con Node.js, sin dependencias ni `npm install`. Descargar
|
||
y ejecutar. Esto elimina el error "Cannot find module 'express'" que veían
|
||
los usuarios nuevos.
|
||
- Los nombres de proceso en "Procesos destacados" se muestran limpios: un
|
||
servicio Node con flags (p.ej. Mempool con --max-old-space-size) aparece
|
||
como "node (index.js)" en vez de mostrar el argumento crudo.
|
||
- La medición de CPU del monitor usa una ventana de 500ms para una lectura
|
||
más estable y coherente con la de herramientas como `top`.
|
||
## [1.8.0] — 2026-06-04
|
||
|
||
### Añadido
|
||
|
||
- Importación de etiquetas BIP-329. Permite cargar el archivo `.jsonl` que
|
||
exporta Sparrow (File → Export → Export Labels) para ver tus propias etiquetas
|
||
junto al análisis. El archivo se lee en memoria del navegador y no sale del
|
||
nodo. La etiqueta de la transacción analizada aparece de forma destacada bajo
|
||
el txid, y el modal lista todas las etiquetas cargadas (referencia + etiqueta)
|
||
para verificar lo que tienes importado.
|
||
- Watch-only por clave pública extendida (xpub/zpub). Pegando el Master Public
|
||
Key de Sparrow, Txoko deriva tus direcciones de recepción y cambio en local y
|
||
marca automáticamente, en cada transacción analizada, qué outputs son tuyos:
|
||
recepción (verde) o cambio (ámbar), con su valor y etiqueta. Es lo que ningún
|
||
explorador público puede decirte, porque no conoce tus direcciones. Toda la
|
||
derivación (BIP32 + bech32) está implementada desde cero con las primitivas
|
||
del navegador (Web Crypto API), sin librerías externas, fiel a la filosofía de
|
||
código auditable. El xpub nunca sale del navegador.
|
||
- Selector del número de direcciones a derivar (20 / 50 / 100 por rama). Más
|
||
direcciones dan más cobertura para wallets muy usados, a cambio de unos
|
||
segundos más de cálculo. Se muestran las tres primeras direcciones derivadas
|
||
para que el usuario verifique, comparándolas con Sparrow, que ha cargado el
|
||
wallet correcto.
|
||
- Estado "Dirección sin usar" en el análisis de direcciones. Una dirección sin
|
||
historial on-chain ya no se muestra como "privacidad aceptable" (que sugería
|
||
erróneamente que era buena), sino como dirección nueva sin nada que analizar.
|
||
|
||
### Cambiado
|
||
|
||
- El informe de privacidad usa el sustantivo correcto ("dirección" o
|
||
"transacción") según lo que se esté analizando, en lugar de decir siempre
|
||
"transacción".
|
||
|
||
### Corregido
|
||
|
||
- Corregida la aritmética de curva secp256k1 usada en la derivación watch-only.
|
||
El inverso modular por el algoritmo de Euclides extendido fallaba en ciertos
|
||
casos con enteros grandes, y la suma de puntos no manejaba el caso punto más
|
||
su inverso. Las operaciones pequeñas (2G, 3G) salían bien pero la derivación
|
||
de direcciones reales producía resultados incorrectos. Resuelto usando el
|
||
inverso por el pequeño teorema de Fermat y completando los casos de la suma de
|
||
puntos. Verificado contra los vectores de prueba estándar de RIPEMD-160,
|
||
bech32 (BIP173) y secp256k1, y contra la identidad de grupo.
|
||
|
||
---
|
||
|
||
## [1.7.0] — 2026-06-04
|
||
|
||
### Añadido
|
||
|
||
- Detección de CoinJoin estructural (WabiSabi y similares). El motor reconocía
|
||
CoinJoin por denominaciones fijas (Whirlpool) o por outputs de igual valor
|
||
(CoinJoin genérico), pero no capturaba WabiSabi, que usa outputs de valor
|
||
variable. Se añadió una tercera variante: detecta mezclas por forma estructural
|
||
(muchos inputs y outputs, ratio cercano a 1, sin output dominante). El
|
||
fingerprint "Wasabi o JoinMarket" se activa también para esta variante.
|
||
|
||
- Check de datos OP_RETURN en el motor de privacidad. Hasta ahora OP_RETURN solo
|
||
se detectaba en la herramienta dedicada de TOOLS. Ahora aparece en el informe
|
||
de privacidad de Auditoría: informa de cuántos outputs OP_RETURN hay y su
|
||
tamaño en bytes, sin mostrar el contenido. Una transacción con OP_RETURN fuerza
|
||
banda BAJA independientemente del resto de señales.
|
||
|
||
- Check de tipo de script legacy (P2PKH/P2SH). Detecta cuando los inputs usan
|
||
direcciones legacy (1... o 3...) en lugar de SegWit (bc1q...) o Taproot
|
||
(bc1p...). Informa de que forman un conjunto de usuarios cada vez más pequeño,
|
||
lo que reduce el anonimato por conjunto. Peso −15, certeza CERTEZA,
|
||
actionability "wallet" (depende del software).
|
||
|
||
### Cambiado
|
||
|
||
- En transacciones CoinJoin, los checks "Mezcla de tipos de script en inputs",
|
||
"Inputs innecesarios (consolidación revelada)" y "Fingerprinting de wallet"
|
||
ahora se muestran como informativos (punto gris, sin penalización) en lugar de
|
||
como advertencias en rojo. En una mezcla, estas condiciones son esperadas por
|
||
diseño del protocolo — no son fallos del usuario.
|
||
|
||
### Corregido
|
||
|
||
- El check de batch payment ya no dispara en transacciones WabiSabi. Al no
|
||
detectarse como CoinJoin, el filtro `!likelyCJ` no se activaba y un WabiSabi
|
||
de 327×279 aparecía marcado como batch. Resuelto al corregir la detección.
|
||
|
||
---
|
||
|
||
## [1.6.0] — 2026-06-03
|
||
|
||
### Añadido
|
||
|
||
- Detección de exchanges (cuarta categoría de entidad, junto a OFAC, minería y
|
||
CoinJoin). Cuando una transacción o un eslabón del rastro toca una dirección
|
||
asociada a un exchange conocido, se marca con 🏦 y aparece en el veredicto y
|
||
en la distancia a entidad del rastro.
|
||
- La detección distingue la fiabilidad de cada dirección según su origen, en
|
||
línea con la honestidad del resto de la herramienta:
|
||
- Direcciones que el propio exchange ha publicado, o de etiquetas públicas
|
||
conocidas, se muestran como coincidencia (certeza PROBABLE).
|
||
- Direcciones inferidas por agrupación (clustering) se muestran como "posible"
|
||
(certeza POSIBLE), avisando claramente de que es una inferencia estadística
|
||
no confirmada por el exchange y de que puede haber falsos positivos.
|
||
- Conjunto inicial de ~590 direcciones de 16 exchanges, mayoritariamente de
|
||
datos de agrupación pública, etiquetadas con su fuente para reflejar su
|
||
fiabilidad.
|
||
|
||
---
|
||
|
||
## [1.5.0] — 2026-06-03
|
||
|
||
### Cambiado
|
||
|
||
- Mejoras de legibilidad del rastro de procedencia, para interpretar los datos
|
||
de un vistazo:
|
||
- Veredicto arriba del rastro: "Camino limpio" (en verde) si nada de lo
|
||
explorado toca entidades marcadas, o "Atención" (en ámbar) indicando qué
|
||
toca y a cuántos saltos. Responde de un vistazo si hay algo que revisar.
|
||
- Jerarquía visual: los eslabones con buena privacidad y sin marcas se
|
||
muestran discretos; los que merecen atención (marca o banda no alta) se
|
||
destacan con color. El ojo va directo a lo relevante.
|
||
- Barra visual de cantidad en cada eslabón, para captar la proporción sin
|
||
leer la cifra; el número queda al lado como apoyo.
|
||
- Indicador de estructura (entradas → salidas) siempre visible junto a cada
|
||
eslabón, útil para detectar consolidaciones grandes de un vistazo.
|
||
- Detalle ampliado al pasar el ratón (o tocar en móvil) sobre el indicador
|
||
de estructura: número exacto de entradas y salidas, valor en sat y BTC,
|
||
bloque, comisión y tasa. Mantiene la vista limpia y el detalle a mano.
|
||
- Contraste de texto mejorado en toda la aplicación. El texto secundario
|
||
(descripciones, datos de apoyo, el texto dentro de las casillas y el pie de
|
||
página) era demasiado tenue y costaba leerlo. Se ha subido su contraste en
|
||
los cuatro temas, manteniendo la jerarquía visual: el texto secundario se
|
||
sigue distinguiendo del principal, pero ahora se lee con comodidad.
|
||
|
||
---
|
||
|
||
## [1.4.0] — 2026-06-02
|
||
|
||
### Añadido
|
||
|
||
- Distancia a una entidad en el rastro de procedencia. A medida que se exploran
|
||
eslabones, un resumen arriba del rastro indica a cuántos saltos está lo más
|
||
cercano de cada tipo marcado (OFAC, minería, CoinJoin) — por ejemplo "minería
|
||
· a 2 saltos". Es honesto sobre su alcance: solo cuenta las ramas que se han
|
||
seguido y avisa de que puede haber más sin explorar.
|
||
|
||
### Corregido
|
||
|
||
- Transacciones coinbase como origen del rastro. Antes mostraban un aviso
|
||
confuso ("este input no indica su transacción de origen"), como si fuera un
|
||
error. Ahora se reconocen como el principio natural del rastro: un mensaje
|
||
claro indica que esas monedas se crearon ahí como recompensa de minado y no
|
||
proceden de ninguna transacción anterior.
|
||
|
||
---
|
||
|
||
## [1.3.0] — 2026-06-02
|
||
|
||
### Añadido
|
||
|
||
- Rastro de procedencia. En la pestaña Auditoría, debajo del informe de
|
||
privacidad, cada input de la transacción tiene un botón "seguir rastro" que
|
||
trae su transacción de origen y muestra un eslabón con: el txid de origen
|
||
(enlace a tu propio Mempool), cuánto fluyó, la fecha, la banda de privacidad
|
||
de esa transacción y marcas relevantes (CoinJoin, OFAC, minería, coinbase).
|
||
- El rastro es recursivo: desde cada eslabón se puede seguir bajando por sus
|
||
propios inputs, encadenando saltos hacia atrás nivel a nivel. Funciona bajo
|
||
demanda —cada consulta la pide el usuario, para no sobrecargar el nodo— y
|
||
comparte una caché en memoria para no repetir transacciones ya consultadas.
|
||
Incluye un límite de profundidad y un botón para limpiar el rastro.
|
||
|
||
### Corregido
|
||
|
||
- Detección de CoinJoin en el rastro: antes marcaba como CoinJoin cualquier
|
||
transacción cuyo análisis mencionara la palabra (lo que ocurría siempre,
|
||
porque existe un indicador con ese nombre aunque dé negativo), produciendo
|
||
falsos positivos. Ahora la marca solo aparece cuando el indicador de CoinJoin
|
||
da positivo de verdad (estructura de varios inputs y salidas de igual valor).
|
||
|
||
---
|
||
|
||
## [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
|
||
|
||
### Añadido
|
||
|
||
- Exportación del informe de privacidad de una transacción en dos formatos,
|
||
desde la pestaña Auditoría. El botón "↓ JSON" descarga el análisis completo
|
||
con su estructura (score, banda, wallets detectados y todas las señales con
|
||
su detalle), pensado para procesar con scripts o archivar en formato máquina.
|
||
El botón "↓ MD" descarga el informe legible en Markdown (diagnóstico, qué
|
||
puede saber un analista, limitaciones, recomendaciones y una tabla de señales),
|
||
para leer o guardar como documento. Ambos se generan en el navegador y se
|
||
descargan en local — no sale nada del nodo.
|
||
|
||
### Corregido
|
||
|
||
- El botón "Analizar privacidad" del Lab ahora envía correctamente el txid a
|
||
la pestaña Auditoría y lanza el análisis. Antes la pestaña aparecía vacía
|
||
porque el identificador no se transmitía entre vistas.
|
||
|
||
---
|
||
|
||
## [1.1.0] — 2026-06-01
|
||
|
||
### Añadido
|
||
|
||
- Exportación del UTXO Map a CSV. Un botón "↓ CSV" en el UTXO Map descarga
|
||
todos los UTXOs de la dirección analizada con su información: txid, vout,
|
||
valor en sats y en BTC, estado de confirmación, altura de bloque, score
|
||
de privacidad y problemas detectados. El archivo se genera en el navegador
|
||
y se descarga en local — no pasa por ningún servidor, no sale nada del nodo.
|
||
Pensado para abrir en una hoja de cálculo y ordenar o filtrar los UTXOs
|
||
por privacidad.
|
||
|
||
---
|
||
|
||
## [1.0.0] — 2026-05-31
|
||
|
||
Primera versión completa y funcional. Suite de auditoría de privacidad
|
||
Bitcoin self-hosted, con motor de análisis, exploración de cadena, monitor
|
||
del nodo y detección de entidades.
|
||
|
||
### Motor de análisis de privacidad
|
||
|
||
- Análisis de transacción con 15+ heurísticas ponderadas
|
||
- Score de privacidad por bandas (ALTA / MEDIA / BAJA), sin precisión
|
||
numérica falsa
|
||
- Informe narrativo en prosa: explica qué puede deducir un observador
|
||
- Distinción explícita entre certeza, probabilidad y posibilidad en cada señal
|
||
- Etiquetas de accionabilidad: qué es evitable, qué depende del wallet,
|
||
qué ya está en la cadena y no se puede cambiar
|
||
|
||
### Detección de CoinJoin
|
||
|
||
- Whirlpool con denominaciones fijas y tolerancia del 2%
|
||
- CoinJoin genérico por outputs de igual valor
|
||
- Filtro de denominación mínima (10.000 sats) — descarta mezclas de polvo
|
||
que no son CoinJoin reales
|
||
- OP_RETURN tratado correctamente: un Whirlpool con OP_RETURN de cambio se
|
||
sigue detectando
|
||
- Supresión de penalizaciones coherente: en una mezcla no penaliza señales
|
||
que son esperadas
|
||
|
||
### Fingerprinting de wallet
|
||
|
||
- Detección de BlueWallet, Bitcoin Core, Sparrow, Electrum y wallets con
|
||
Taproot nativo a partir de señales estructurales combinadas
|
||
- Wallet inferido del tipo de CoinJoin (Whirlpool → Samourai/Sparrow,
|
||
genérico → Wasabi/JoinMarket)
|
||
- En CoinJoin el fingerprinting es informativo y no penaliza
|
||
|
||
### Detección de entidades
|
||
|
||
- Índice de ~155 direcciones embebido, lookup local sin consultas externas
|
||
- OFAC: alerta crítica si una dirección está en la lista de sanciones
|
||
(Blender.io, Sinbad, Garantex, Suex, Chatex, Lazarus Group)
|
||
- Mining pools: marca informativa de origen en pool de minería
|
||
(F2Pool, Foundry, Antpool, MARA, Braiins, Luxor, Ocean, NiceHash, Bitfury)
|
||
|
||
### Exploración y monitor
|
||
|
||
- Explorador de bloques y búsqueda on-chain
|
||
- Vista de Mempool con stats, fees y bloques proyectados
|
||
- Monitor del nodo: estado de Bitcoin Core, sistema y logs en tiempo real
|
||
- UTXO Map con score de privacidad por moneda
|
||
- Herramientas: conversor de unidades, validador de dirección, decodificadores
|
||
|
||
### Experiencia de uso
|
||
|
||
- El análisis se mantiene al cambiar de pestaña y volver (Privacy Lab y
|
||
Auditoría) — el estado vive en memoria, sin escribir en disco
|
||
- Diseño responsive para móvil
|
||
- Tema cypherpunk
|
||
|
||
### Filosofía
|
||
|
||
- Sin dependencias externas de datos: todo viene del propio nodo del usuario
|
||
- Ninguna consulta sale de la red del usuario
|
||
- Didáctica honesta sobre las limitaciones del análisis
|
||
|
||
[1.0.0]: https://git.bitcointxoko.org/pikaro/txoko-dashboard
|