Datos en cadena y SQL · Lección 3 de 9
El modelo UTXO para analistas
10 min de lectura
Bitcoin y las cadenas derivadas de él (Litecoin, Bitcoin Cash, Dogecoin y otras) no almacenan saldos. Almacenan salidas discretas que una transacción crea y otra destruye. Cada saldo, flujo, rastreo y agrupación que un analista genera para estas cadenas se basa en esa estructura, y la mayoría de los errores de principiantes (doble contabilización, saldos fantasma, «pagos» que en realidad eran cambio) se deben a olvidarla.
Las monedas son salidas
Una salida de transacción es un importe más un script de bloqueo (el scriptPubKey). Se identifica mediante su outpoint: el txid de la transacción que la creó y su posición en la lista de salidas de esa transacción, escrito como txid:vout. Una salida solo tiene dos estados: no gastada o gastada, y se gasta exactamente una vez y en su totalidad. No existe el gasto parcial ni una «cuenta» de la que retirar fondos.
Una entrada no contiene un importe. Apunta a un outpoint anterior y proporciona los datos (firma, clave pública, script) que satisfacen el script de bloqueo de esa salida. Para conocer el valor de una entrada, hay que consultar la salida que gasta (véase la lección Transacciones de Bitcoin en bruto y tipos de scripts).
De esto se derivan tres consecuencias para el análisis:
| Lo que ven los usuarios | Lo que registra la cadena |
|---|---|
| «Mi dirección contiene 1,2 BTC» | Varias salidas no gastadas cuyos scripts se decodifican como esa dirección y que suman 1,2 BTC |
| «Envié 0,5 BTC» | Una transacción que consumió salidas completas y creó otras nuevas, por lo general con cambio incluido |
| «Mi saldo disminuyó» | Las salidas pasaron del conjunto de salidas no gastadas al de salidas gastadas |
Por lo tanto, el saldo de una dirección es una cifra derivada, nunca una cifra almacenada. Las monedas nuevas entran a través de la transacción coinbase, que no tiene entradas reales y paga el subsidio del bloque más las comisiones. Las salidas coinbase solo pueden gastarse en un bloque situado al menos 100 bloques después del que las creó, una regla denominada madurez de coinbase.
El grafo de gasto
Como cada entrada apunta exactamente a una salida anterior, la cadena forma un grafo de gasto. Este es un pequeño ejemplo decodificado:
{
"txid": "b7e0…5d19",
"vin": [
{ "txid": "4f3a…c21e", "vout": 0 },
{ "txid": "9b1d…07aa", "vout": 1 }
],
"vout": [
{ "n": 0, "value": 1.00000000, "address": "3J9x…Qe1m" },
{ "n": 1, "value": 0.09950000, "address": "bc1q…r8tn" }
]
}
La transacción b7e0…5d19 gasta la salida 0 de 4f3a…c21e (0,80 BTC) y la salida 1 de 9b1d…07aa (0,30 BTC). Las entradas suman 1,10 BTC; las salidas suman 1,0995 BTC; la comisión es la diferencia: 0,0005 BTC. Las dos salidas ahora están sin gastar y solo se convertirán en aristas cuando algo las gaste.
Interpretado como un grafo, las transacciones son nodos y las salidas son aristas dirigidas desde la transacción que las creó hasta la transacción que las gastó. Las salidas no gastadas son aristas que aún no tienen destino. El grafo es acíclico y siempre apunta hacia adelante en el tiempo, por lo que cualquier satoshi puede seguirse hacia atrás, salto a salto, hasta las transacciones coinbase que lo crearon.
Una propiedad importa más que cualquier otra: dentro de una transacción, el protocolo no vincula entradas específicas con salidas específicas. Cuando entran 0,80 y 0,30 BTC y salen 1,00 y 0,0995, nada en la cadena indica qué entrada «pagó» qué salida. El valor se combina y se redistribuye. Todo método de rastreo que asigna entradas a salidas (primero en entrar, primero en salir; reparto proporcional; etc.) es una convención del analista, no un hecho extraído de los datos; la lección Métodos de rastreo y sus consecuencias los compara.
Salidas de cambio
Como las salidas se gastan completas, un pago suele enviar parte del valor al destinatario y devolver el resto al remitente como cambio. En el ejemplo anterior, una de las dos salidas probablemente sea cambio, pero el protocolo no indica cuál. Identificarla es una cuestión de heurísticas (tipo de script, importes redondos, si la dirección es nueva), que se abordan en la lección Heurísticas de detección del cambio.
Identificar incorrectamente el cambio tiene consecuencias importantes. Si se trata el cambio como un pago, se inventa una contraparte y se dirige el rastreo por la rama equivocada. Si se contabilizan todas las salidas como valor transferido, el «volumen» en cadena queda inflado, porque el cambio y las transferencias a uno mismo no mueven valor entre partes. Las mediciones rigurosas del volumen intentan excluir el cambio y las consolidaciones, y la calidad de cada una depende de su modelo de cambio.
Reconocer algunas formas habituales de transacción resulta útil:
| Forma | Estructura típica | Significado habitual |
|---|---|---|
| Pago simple | 1–3 entradas, 2 salidas | Pago más cambio |
| Consolidación | Muchas entradas, 1 salida | Una billetera que combina salidas pequeñas, a menudo cuando las comisiones son bajas |
| Pago por lotes | Pocas entradas, muchas salidas | Un servicio que paga a muchos clientes a la vez |
| Paso de una cadena de desprendimiento | 1 entrada, 2 salidas: una pequeña y una grande | Parte de una cadena de desprendimiento; la salida grande continúa (véase la lección Grafos de transacciones y métricas de grafos) |
Antigüedad de las monedas y dormancia
Cada salida registra el bloque en el que se creó, por lo que toda salida no gastada tiene una antigüedad. Cuando se gasta una salida, los analistas calculan los días-moneda destruidos (CDD): el importe multiplicado por el número de días que permaneció sin gastar. Una salida de 2 BTC creada 400 días antes destruye 800 días-moneda al gastarse. El total de CDD de un día dividido por los BTC movidos ese día proporciona una dormancia media: cuánto tiempo, en promedio, habían permanecido inmóviles las monedas que se movieron.
La antigüedad importa en las investigaciones porque las monedas que se mueven de repente tras un largo período de inactividad constituyen una alerta clásica. Gran parte de los bitcoines robados a Bitfinex en 2016 permaneció en la billetera del atacante durante años, hasta que las autoridades incautaron unos 94.000 BTC en 2022. Vigilar las salidas vinculadas a un incidente antiguo para detectar su primer gasto es una de las reglas más sencillas y valiosas que puede configurar un analista.
Un desglose del conjunto UTXO por antigüedad, a una altura fija, tiene este aspecto en SQL. El esquema es el genérico utilizado en todo este módulo (lecciones 2.7 y 2.9): una tabla outputs con una fila por salida y una tabla inputs en la que cada fila identifica el outpoint que gasta (spent_transaction_hash, spent_output_index); los valores están en satoshis.
-- Salidas no gastadas a la altura 900.000, agrupadas por antigüedad (unos 144 bloques al día)
WITH spent AS (
SELECT spent_transaction_hash, spent_output_index
FROM inputs
WHERE block_number <= 900000
)
SELECT
CASE WHEN 900000 - o.block_number < 144 * 30 THEN '1: menos de 1 mes'
WHEN 900000 - o.block_number < 144 * 365 THEN '2: 1-12 meses'
ELSE '3: más de 1 año' END AS age_band,
COUNT(*) AS utxos,
SUM(o.value) / 100000000.0 AS btc
FROM outputs o
LEFT JOIN spent s
ON s.spent_transaction_hash = o.transaction_hash
AND s.spent_output_index = o.output_index
WHERE o.block_number <= 900000
AND s.spent_transaction_hash IS NULL -- sin gasto a esa altura o antes
GROUP BY 1
ORDER BY 1;
La consulta conserva únicamente las salidas creadas a la altura elegida o antes que no tengan un gasto coincidente a esa altura o antes, lo que constituye la definición del conjunto UTXO en ese punto. Los intervalos de antigüedad utilizan el número de bloques como aproximación del tiempo; para obtener antigüedades precisas, haga una unión con la tabla de bloques y utilice las marcas de tiempo de los bloques (teniendo en cuenta las salvedades sobre las marcas de tiempo de la lección Bloques, encabezados y API de nodos). Observe el divisor 100.000.000,0: la división entera truncaría el resultado sin avisar en algunos motores.
El conjunto UTXO
El conjunto de todas las salidas no gastadas en un bloque determinado es el conjunto UTXO. Los nodos lo mantienen en una base de datos específica (el chainstate) porque es lo que necesitan para validar transacciones nuevas. Ha alcanzado un tamaño considerable, impulsado en parte por experimentos con inscripciones y tokens que crearon muchas salidas pequeñas, por lo que las consultas sobre él son costosas.
Aquí resultan útiles dos métodos RPC de Bitcoin Core:
gettxoutsetinfodevuelve estadísticas resumidas del conjunto, incluidostxouts(el número de salidas no gastadas) ytotal_amount(las monedas que contienen). Consultar una altura histórica requiere el índice opcional coinstatsindex.scantxoutsetbusca en el conjunto UTXO actual salidas que coincidan con descriptores de salida comoaddr(...)y devuelve cada coincidencia con su txid, vout, importe y altura. Solo ve salidas no gastadas, nunca el historial.
Bitcoin Core no mantiene un índice de direcciones, por lo que obtener el historial completo de transacciones de una dirección requiere un indexador o un almacén de datos (véase la lección Bloques, encabezados y API de nodos).
El conjunto también permite realizar una comprobación de coherencia. Su total_amount debería ser cercano, pero inferior, al subsidio total emitido, porque parte del valor es demostrablemente imposible de gastar y nunca entra en el conjunto: la recompensa del bloque génesis, las salidas OP_RETURN y las comisiones o el subsidio que los mineros no reclamaron. Un proceso de datos cuyo total UTXO calculado supera la emisión tiene un error, normalmente gastos ausentes o salidas duplicadas.
Reutilización de direcciones
Las direcciones no son cuentas. Las billeteras que siguen las buenas prácticas derivan una dirección nueva para cada recepción a partir de una semilla determinista jerárquica, y el usuario nunca ve la mayoría de ellas. Una persona puede controlar miles de direcciones; una dirección de billetera caliente de un exchange puede interactuar con millones de contrapartes.
La reutilización de direcciones (recibir varias veces en la misma dirección) es habitual de todos modos: direcciones para donaciones, algunas direcciones de depósito de exchanges, billeteras antiguas y usuarios descuidados. Para un analista es tanto una ventaja como una trampa. La reutilización vincula pagos entre sí sin ninguna heurística y, una vez que se ha gastado desde una dirección, su clave pública queda visible en la cadena. Pero las métricas a nivel de dirección, como «direcciones activas» o «direcciones con más de 1 BTC», miden scripts, no personas, y nunca deben presentarse como recuentos de usuarios.
Una sutileza: la misma clave pública puede producir varias direcciones diferentes, una por tipo de script (por ejemplo, P2PKH heredado y P2WPKH de SegWit nativo). Parecen no estar relacionadas, pero una vez que la clave se revela en un gasto, un analista puede vincularlas mediante la propia clave pública.
Enfoque del examen: una salida se identifica por su outpoint (txid:vout) y se gasta una sola vez y en su totalidad; las entradas no contienen un importe, por lo que los valores de las entradas y los saldos se derivan de las propias salidas; el protocolo no vincula entradas específicas con salidas específicas, por lo que el rastreo de entradas a salidas es una convención; las salidas de cambio y por qué clasificarlas incorrectamente distorsiona los rastreos y el volumen; la madurez de coinbase de 100 bloques, y los días-moneda destruidos (importe × días sin gastar) y la dormancia como alertas; el cálculo del conjunto UTXO a una altura determinada (creadas a esa altura o antes, no gastadas a esa altura o antes);gettxoutsetinfoyscantxoutsety sus límites; la reutilización de direcciones y por qué los recuentos de direcciones no son recuentos de usuarios.
Preguntas de práctica
Comprueba lo que has aprendido. Estas preguntas no cuentan para el examen.
Inicia sesión para guardar tu progreso y continuar donde lo dejaste. Es gratis. Iniciar sesión