feat: cierra las cinco heurísticas ausentes del repaso

La comisión como huella: una comisión absoluta redonda o una tarifa entera no
salen de un estimador, salen de alguien que las escribió a mano. Informativo y
sin penalizar, igual que RBF — castigarlo empujaría a elegir comisiones peores
para disimular.

Cambio con más de dos salidas, solo el caso inequívoco: si exactamente una
salida comparte tipo con las entradas no hay nada que adivinar, y con más
salidas la coincidencia por azar es todavía más improbable. POSIBLE, no
PROBABLE, porque es una sola señal.

Autoenvíos en el informe de wallet: solo cabe ahí, porque saber que todas las
salidas son tuyas exige conocer el wallet entero. Avisa solo cuando ataron más
de una dirección — mover una a otra no vincula nada nuevo.
This commit is contained in:
Aitor
2026-08-08 19:03:08 +02:00
parent 3d490b32d4
commit afebeeaccd
4 changed files with 237 additions and 5 deletions
+38
View File
@@ -160,6 +160,44 @@ ok("se etiqueta como consolidación, con certeza y no como probable",
ok("no se cobra dos veces: la penalización la lleva input_linkage",
cu?.penalty === 0 && check(consol, "input_linkage")?.pass === false);
console.log("\n=== La comisión como huella (check nuevo) ===\n");
// Comisión absoluta redonda: nadie llega a 10.000 sats clavados con un
// estimador, que calcula tarifa por tamaño y devuelve números feos.
const feeRedonda = analyzeTx(TX(
[IN(5000000, "v1_p2tr", "bc1pA")],
[OUT(4990000, "v1_p2tr", "bc1pB")],
{ fee: 10000, weight: 600 }
));
ok("detecta una comisión absoluta redonda",
check(feeRedonda, "fee_fingerprint")?.pass === true &&
/10000 sats exactos/.test(check(feeRedonda, "fee_fingerprint")?.detail || ""),
check(feeRedonda, "fee_fingerprint")?.detail?.slice(0, 90));
// Tarifa entera: 20,00 sat/vB sale de teclear "20" en la casilla.
const feeEntera = analyzeTx(TX(
[IN(5000000, "v1_p2tr", "bc1pA")],
[OUT(4996000, "v1_p2tr", "bc1pB")],
{ fee: 3000, weight: 600 } // vsize 150 -> 20,00 sat/vB
));
ok("detecta una tarifa prácticamente entera",
/prácticamente un número entero|sats exactos/.test(check(feeEntera, "fee_fingerprint")?.detail || ""),
check(feeEntera, "fee_fingerprint")?.detail?.slice(0, 90));
// Un estimador automático deja números feos.
const feeEstimada = analyzeTx(TX(
[IN(5000000, "v1_p2tr", "bc1pA")],
[OUT(4996873, "v1_p2tr", "bc1pB")],
{ fee: 3127, weight: 601 }
));
ok("no ve huella donde hay un número feo de estimador",
/sin forma de cifra elegida a mano/.test(check(feeEstimada, "fee_fingerprint")?.detail || ""),
check(feeEstimada, "fee_fingerprint")?.detail?.slice(0, 90));
ok("la huella de comisión no penaliza (misma decisión que con RBF)",
check(feeRedonda, "fee_fingerprint")?.penalty === 0 &&
check(feeRedonda, "fee_fingerprint")?.informational === true);
console.log("\n=== Guardia de CoinJoin ===\n");
// Whirlpool: 5 entradas, 5 salidas de la misma denominación.