Saltar al contenido

Diseccionando transacciones Bitcoin

Si eres un curioso empedernido y te gusta entender las cosas a bajo nivel, como es mi caso, seguro que te habrás preguntado más de una vez que aspecto tiene una transacción de Bitcoin. De ser así, enhorabuena, este articulo es para ti. Así que ponte cómodo y sígueme en esta ruta donde cubriremos desde el más alto de los niveles (la transacción que podrías ver en un explorer) hasta que significa cada uno de los campos y cómo están organizados.

Las bases 

No os voy a engañar, este articulo va a tocar conocimientos técnicos, pero voy a intentar hacerlo de la forma más amigable posible. Aun así, hay algunas bases que deberemos sentar antes de empezar a hablar de transacciones. 

Bits y Bytes

Una transacción no es más que una colección de bytes organizados de una forma concreta. Esos bytes representan información, y se pueden codificar en forma de texto, facilitando así su legibilidad. ¿Pero, qué es un byte?  Para poder definirlo, debemos definir primero qué es un bit.

Un bit es la unidad mínima de almacenamiento de información, este puede almacenar un único valor, siendo 0 o 1. Esto hace que si queremos representar un valor superior a uno en bits, necesitaremos usar más de un bit. Podemos ver los bits como contenedores de información, a más bits, más información podemos almacenar. 

Al agrupar bits entre sí ganamos más información, pero esta ganancia no se obtiene de forma lineal, sino de forma exponencial. Si un bit nos permite almacenar 2 valores, dos bits nos permiten almacenar 4 valores, y tres bits nos permiten almacenar 8 valores. El crecimiento se produce en potencias de dos:

1 bit -> 21 -> 2 valores

2 bits -> 22 -> 4 valores

3 bits -> 23 -> 8 valores

4 bits -> 24 -> 16 valores

Entonces, si un bit es la unidad mínima de información que podemos almacenar, ¿qué es un byte? 

Un byte es una agrupación de 8 bits, y es la unidad mínima de almacenamiento en informática. Esta nos permite almacenar hasta 256 valores distintos (0-255). De esa forma, si necesitamos almacenar cualquier valor de 0 a 255, necesitaremos un byte. Es importante entender que indiferentemente de que el valor que queramos almacenar ocupe 1, 2, 3, …, o 8 bits, necesitaremos un byte completo para almacenarlo, ya que este es nuestro contenedor más pequeño.

Hexadecimal

Ahora que sabemos que son los bit y los bytes, hablemos de cómo podemos representarlos. La forma más habitual en la que nosotros, las personas, vemos representados los valores es en decimal, es decir, repeticiones de números del 0-9. Sin embargo, esta no es, ni de lejos, la forma más habitual en que las máquinas representan estos valores. Los valores en bits se acostumbran a ver en binario, es decir, colecciones de ceros y unos (como ya podíais intuir en la sección anterior).

Representar valores en binario ocupa mucho espacio, y es muy poco intuitivo. Si para representar el número 21000000 os tuviera que escribir 1010000000110111101000000 nos tiraríamos toda la vida con este articulo. Es por eso que para representar bytes no se acostumbra a usar binario, sino hexadecimal.

El hexadecimal es el sistema numérico que se acostumbra a usar en informática, y el que se usa para codificar las transacciones de Bitcoin cuando se le muestran a un usuario (lo mismo pasa con claves publicas y privadas, identificadores de transacción o de bloque, invoices de Lightning Network, etc). A diferencia del decimal, donde se utilizan 10 valores (0-9), el hexadecimal utiliza 16 (0-9 y de A – F). De esta forma, el número 15 se representa como F, y el 16 como 10.

Un punto importante a tener en cuenta es que cada carácter hexadecimal ocupa 4 bits (medio byte), por lo tanto, un byte nos permite almacenar dos caracteres hexadecimales.

Si queréis experimentar un poco con transformaciones entre binario, decimal y hexadecimal, os recomiendo esta web.

Endianness 

Finalmente, nuestro última parada en el camino antes de empezar a hablar del tema que nos atañe es la organización de colecciones de más de un byte, lo que normalmente se conoce como “extremidad”, o endianness.

Existen, históricamente, dos formatos distintos para agrupar cadenas de bytes: el Big-Endian (BE) y el Little-Endian (LE). Estos formatos describen, básicamente, en que orden se almacenan estos bytes en memoria, y por lo tanto, como viajan por la red de Bitcoin cuando se comunican entre nodos. En lugar de explicar qué significa cada formato de forma teórica, veámoslo con un ejemplo para que quede más claro.

Supongamos que tenemos cuatro bytes de información que forman el siguiente mensaje en hexadecimal: 0A0B0C0D. La disección en bytes del mensaje sería 0A (10 en decimal), 0B (11 en decimal), 0C (12 en decimal) y 0D (13 en decimal). Ahora bien, si queremos guardar este mensaje byte a byte, ¿por dónde deberíamos empezar? Como hemos comentado anteriormente, la memoria está organizada en contenedores de 1 byte, por lo tanto podríamos tener:

Diagrama Little-Endian
Diagrama Big-Endian

El primer formato es el conocido como Little-Endian, ya que empieza a almacenar los datos desde el valor menos significativo (el de la derecha del todo, 0D), mientras que el segundo formato es conocido como Big-Endian, ya que empieza a almacenar los datos desde el byte más significativo (el de la izquierda del todo, 0A).

Vale la pena remarcar que la forma más habitual de ver valores hexadecimales codificado es en Big-Endian, ya que es la que más se asemeja a nuestro modo (humano) de representar valores en decimal.

Bitcoin transactions: From Zero to Hero

Si he conseguido no perderos en el camino, enhorabuena, es hora de hablar de transacciones. Para nuestro análisis, utilizaremos la misma transacción en todos los casos. Por ahora, lo único que conocemos de esa transacción es su identificador: cc3d798adc3e3980f45223d94446ffb04dc7dee6818eedf4fe1d9c93cc51fa33

Como os he prometido al principio, vamos a ir analizando transacciones desde el más alto nivel (más simple) hasta el más bajo (más complejo). Empezaremos, pues, por las transacciones vistas en un explorador.

Transacciones en un explorer

La forma más mundana de ver una transacción es en un explorador, y de esos hay decenas. No cubriremos todos los exploradores, ni mucho menos, dado que este simplemente será nuestro punto de partida. En nuestro caso, usaremos blockstream.info como ejemplo.

Primera parte de la transición a analizar en blockstream.info

La primera sección que veremos una vez consultado nuestro id de transacción en el explorador contiene varios campos, pasemos a analizarlos:

STATUS o estado nos indica el número de confirmaciones que tiene nuestra transacción (en caso de tener alguna). Podemos ver que, actualmente, el contador está en 60068, número que variará en función de cuando estés leyendo esto.

INCLUDED IN BLOCK nos indica en que bloque se confirmó esta transacción (en caso de estar confirmada) mostrándonos el block hash en concreto.

BLOCK HEIGHT nos indica la altura de ese bloque en la cadena de bloques. 606378, en este caso.

BLOCK TIMESTAMP hace referencia a la marca de tiempo del bloque que mencionábamos anteriormente, es decir, aproximadamente cuando fue generado.

TRANSACTION FEES muestra la cantidad de fees que la pagó la transacción para incluirse en el bloque, tanto el valor total como el fee rate.

SIZE, VIRTUAL SIZE Y WEIGHT UNITS hacen referencia a la medida de la transacción, en bytes, bytes virtuales y unidades de peso. En este articulo no entraremos en detalle a describir estos tres campos, pero si tienes curiosidad, te recomiendo que escuches los pods L54 y L58 donde trato el tema en detalle con Lunaticoin.

VERSION corresponde a la versión de la transacción, que en este caso es 1. La versión de la transacción, entre otras cosas, nos definirá cómo se organiza la transacción y que podemos encontrar en ella.

LOCK TIME se utiliza para definir candados de tiempo, haciendo que la transacción solamente sea valida a partir de un cierto instante. Candados similares a estos son los que nos permiten construir cláusulas complejas, como en el caso de Lightning Network. En este caso vemos que el valor es 0 indicando que no hay ningún candado definido.

SEGWIT FEE SAVINGS es un campo particular del explorador de Blockstream que nos indica si esta transacción ha sido optima en función de tamaño, o si podría haber ahorrado fees utilizando SegWit. En este caso, podemos ver como podría haber sido más eficiente si hubiera utilizado SegWit.

PRIVACY ANALYSIS es, también, un campo particular del explorador de Blockstream que nos indica si esta transacción ha reutilizado direcciones de Bitcoin, reduciendo así la privacidad del usuario y facilitando el análisis de la cadena. En este caso podemos que, en efecto, esta transacción reutiliza direcciones.

Segunda parte de la transición a analizar en blockstream.info

Seguidamente encontramos una nueva sección, donde podemos ver un primer valor que corresponde con nuestro transaction id. Esto nos identifica de forma única la transacción dentro de la cadena de bloques.

Podemos ver que en esta nueva sección encontramos valores separados por columnas: uno a la izquierda, y dos a la derecha. El valor de la izquierda corresponde a las entradas de la transacción (una única entrada en este caso), mientras que el de la derecha corresponde a las salidas (dos salidas en este caso). Estos nos muestra, a grandes rasgos, de dónde vienen los fondos consumidos por esta transacción y hacían donde van.

Con respecto a las entradas, podemos ver el valor 67760645884791eefdc018e5ee843298d2e76aa6bdc7f2f59381363c3486db29:1, que en realidad son dos valores separados por dos puntos (:). El primer valor nos indica que la entrada de esta transacción está gastando una salida de la transacción 67760645884791eefdc018e5ee843298d2e76aa6bdc7f2f59381363c3486db29, y el segundo valor nos indica que esa salida corresponde al índice 1.

Además, podemos ver que la transacción mueve 0.14545979 BTC.

Por otro lado, en el bloque de las salidas, podemos ver como 0.04993898 BTC van a la dirección 1JKRgG4F7k1b7PbAhQ7heEuV5aTJDpK9TS  y 0.09545979 BTC a la dirección 1AwsGqEXTVeC7oDMyRAsGwXoafYiCFnb6w.

Podríamos seguir exprimiendo un poco más el explorador de Blockstream, haciendo click en el desplegable DETAILS y analizando la información avanzada que nos ofrece. Sin embargo, con la información que tenemos ahora nos basta por lo que a disección básica de transacciones se refiere. 

De este pequeño análisis podríamos derivar que una transacción consta de los siguientes campos:

  • confirmations
  • block_hash
  • block_height
  • block_timestamp
  • fees
  • transaction_size
  • version
  • locktime
  • transaction_id
  • inputs
    • prev_tx_id
    • prev_tx_index
    • value
  • outputs
    • value
    • bitcoin_address

Sim embargo, puedo avanzarte que la realidad dista mucho de esa definición. Antes de ver qué campos realmente forman parte de una transacción y cuales no, me gustaría que tomases un momento para apuntar cuales de ellos crees que forman parte, así, más adelante, podrás comprobar cómo de acertado has estado.

Llegados a este punto, es hora de profundizar un poco más en nuestra disección de transacciones, ahora utilizando nuestro nodo en vez de un explorador externo.

Transacciones en bitcoind

Es hora de utilizar nuestro nodo para saber un poco más acerca de la transacción que estamos analizando. Si no tienes un nodo, no te preocupes, toda la información necesaria estará en esta sección, aun que tal vez sea un buen momento para plantearte tener uno.

Para preguntar a nuestro nodo acerca de la transacción, podemos utilizar el comando getrawtransaction.

bitcoin-cli getrawtransaction cc3d798adc3e3980f45223d94446ffb04dc7dee6818eedf4fe1d9c93cc51fa33 1

Ten en cuenta que, a no ser que tu nodo tenga la indexación de transacciones activada (txindex=1), no obtendremos respuesta. Esto sucede por que la transacción no ha sido creada por nuestro wallet, por lo que nuestro nodo no tiene una forma rápida de buscarla. Los exploradores de cadena utilizan nodos con indexación de transacciones activado, de ahí que podamos consultar cualquier transacción.

En caso de no tener un nodo propio, o de que nuestro nodo no indexe transacciones, podemos utilizar herramientas como ChainQuery para realizar la consulta.

getrawtransaction en ChainQuery

Deberemos marcar Verbose = True para conseguir el resultado que nos interesa.

Ya sea utilizando nuestro nodo, o ChainQuery, obtendremos el mismo resultado:

Resultado de getrawtransaction en bitcoind

Podemos ver que el resultado es bastante extenso, así que analicémoslo por bloques.

Primer bloque a analizar (bitcoind)

Podemos ver qué, en este primer bloque, la mayoría de los campos corresponden a los que ya habíamos analizado en la sección de exploradores de cadena.

txid corresponde al identificador de transacción, y en este caso es igual al hash. Esto es debido a que esta transacción no es SegWit, como ya hemos podido ver en el apartado anterior.

Podemos encontrar, además, el campos version, locktime, y los diferentes campos de tamaño de la transacción que ya hemos definido anteriormente.

Segundo bloque a analizar (bitcoind), entradas

El siguiente bloque de la transacción a analizar es la correspondiente al bloque de entradas (vin, inputs vector o vector de entradas). Este corresponde a una lista de entradas con la información acerca de la procedencia de los fondos consumidos por esta transacción. En este caso, como hemos visto anteriormente, solo encontramos una entrada.

Dentro de la única entrada que tenemos podemos encontrar los campos txid y vout haciendo referencia, respectivamente, al identificador de transacción e índice de salida que esta entrada está consumiendo (UTXO, Unspent Transaction Output, o salida no gastada de una transacción).

Seguidamente podemos ver el campo scriptSig. Este campo incluye la demostración de que quienquiera que intentó crear esta transacción tenia derecho a hacerlo. Esto normalmente se demuestra con una firma digital, como en este caso. Dentro de scriptSig encontramos dos campos adicionales: asm y hex. asm nos define los campos por separado, mientras que hex los muestra codificados tal cual los encontramos en la transacción (más sobre esto en la siguiente sección). Si prestamos atención al campo asm, veremos que está formado por tres campos:

X[Y] Z

X corresponde a la firma digital de la transacción:

3045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db

Y corresponde al tipo de firma que se ha realizado: ALL, es decir, todos los campos de la transacción se han firmado (más sobre esto en la siguiente sección).

Finalmente, Z corresponde a la clave pública necesaria para verificar la firma digital de la transacción:

033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314

El último campo que podemos ver en el bloque de las entradas es sequence, que hace referencia al número de secuencia de la entrada. Este campo se utiliza para marcar una transacción como final o no. Una transacción marcada como no final puede ser reemplazada siempre y cuando se encuentre en la mempool. En este caso, el valor es 4294967295, que corresponde al valor hexadecimal FFFFFFFF. Esto nos indica que la transacción había sido marcada como final, ya que las transacciones no finales necesitan un número de secuencia estrictamente inferior a FFFFFFFF.

Tercer bloque a analizar (bitcoind), salidas.

El siguiente bloque a analizar son las salidas. Como ya habíamos visto anteriormente, esta transacción contiene dos salidas:

La primera de ellas con un valor de 0.04993898 BTC e índice 0. De forma similar a las entradas, podemos encontrar un campo llamado scriptPubKey que contiene la información relativa a hacia donde se mueven los fondos de la transacción. Podemos ver como scriptPubKey también contiene los campos asm y hex que nos definen las condiciones de gastado de estas nuevas salidas. En este articulo no entraremos en detalle a explicar que significa cada uno de los campos de asm. Por ahora, es suficiente con saber qué definen el script de gastado de esta salida. Si estás interesado acerca de script, estate atento a EB, más adelante publicaré otro articulo tratando scripting en detalle.

Seguidamente, podemos ver como el número de firmas necesarias para gastar esta salida será 1 (campo reqSigs), como la salida es de tipo pubkeyhash (es decir, estamos creando una salida de tipo P2PKH) y la dirección de Bitcoin que se deriva de ese script es 1JKRgG4F7k1b7PbAhQ7heEuV5aTJDpK9TS (campo address).

La segunda de las salidas es prácticamente idéntica a la primera. El valor es 0.09545979 BTC en este caso, y el índice es 1, pero la salida también es de tipo P2PKH y requiere una única firma. El script es ligeramente diferente, enviado los fondos a la dirección 1AwsGqEXTVeC7oDMyRAsGwXoafYiCFnb6w en este caso.

Último bloque a analizar (bitcoind)

Finalmente, el último bloque a analizar contiene algunos campos ya conocidos, y otros que aun no habíamos visto. Analizaremos este bloque a la inversa.

Los campos blocktime y time nos indican el timestamp de generación del bloque, como ya habíamos visto en la sección anterior, aún que esta vez en un formato distinto. Esta es la forma habitual (y más reducida) de representar timestamps: números de segundos transcurridos desde la medianoche del 1 de enero de 1970 (UTC), o lo que es lo mismo, el timestamp epoch.

El campo blockhash nos indica el hash del bloque que confirmó la transacción, como ya habíamos visto.

Finalmente, el último campos a analizar es el campo hex, que coincide con la representación hexadecimal de la transacción. Este campo no lo habíamos visto anteriormente, y representa la forma real de la transacción.

Si comparamos los campos que hemos visto en este análisis con los campos vistos en el análisis de explorador, podremos ver que muchos coinciden, pero existen campos que solo encontramos en una de las versiones. Llegados a este punto, te recomiendo que revises los campos que pensabas que formaban parte de la transacción y añadas o elimines los que creas convenientes.

Transacciones en realidad

Al final de la sección anterior hemos obtenido la representación real de una transacción codificada en hexadecimal, así que ya es hora de empezar a mancharse las manos y hacer una disección campo a campo.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

Dado que intentar analizar la transición anterior sin ningún tipo de guía sería como dar palos de ciego, será mejor que empecemos por ver que campos forman realmente parte de una transacción y cuánto ocupa cada uno.

Estructura de una transacción

Una transacción esta formada por los siguientes campos (en orden de aparición):

  • versión, de 4 bytes de longitud
  • número de entradas, de medida variable
  • entradas (se repite por cada entrada)
    • identificador de la transacción anterior, de 32 bytes de longitud
    • índice de la salida a gastar, de 4 bytes de longitud
    • tamaño del script de entrada, de tamaño variable
    • script de entrada, de tamaño variable
    • número de secuencia, de 4 bytes de longitud
  • número de salidas, de tamaño variable
  • salidas (se repite por cada salida)
    • valor de la salida, de 8 bytes de longitud
    • tamaño del script de salida, de tamaño variable
    • script de salida, de tamaño variable

Además, es importante remarcar que todos los campos en la transacción están codificados en Little-Endian, mientras que los campos que acostumbramos a ver tanto en un explorador como en la respuesta a getrawtransaction están codificados en Big-Endian.

Ahora sí, podemos empezar a diseccionar nuestra transacción. Empecemos por el numero de versión, para ello debemos seleccionar los 4 primeros bytes de la transacción (u ocho caracteres hexadecimales):

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

01000000 (o 01000000 en BE) se traduce a 1 en decimal, y corresponde con el número de versión que hemos visto tanto en nuestro análisis con explorador como con bitcoind.

El siguiente campos a extraer es el número de entradas. Como podemos ver en el diagrama de estructura de la transacción, y como hemos comentado anteriormente, el número de entradas es un campo de longitud variable. Los campos de longitud variable en Bitcoin se codifican en un formato VARINT propio. En este articulo no entraremos a hablar de dicho formato, bastará con mencionar que, mientras el número a codificar sea de un solo byte (0-255) la codificación corresponderá al valor hexadecimal.

Ente caso, sabemos que el número de entradas corresponde a 1, por lo que deberemos extraer un único byte de la transacción.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

El valor corresponde a 01, que se traduce, tal y como esperábamos, a 1 en decimal.

Llegados a este punto debemos empezar a extraer las entradas de la transacción. No existe ningún tipo de limitador de entradas, sabiendo el número de entradas y lo que ocupa cada campo tenemos suficiente.

El primer campo a extraer de nuestra única entrada es el identificador de transacción anterior, este ocupa 32 bytes, o 64 caracteres hexadecimales.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

Por lo tanto, podemos ver como el prev_tx_id es 29db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667. Si repasamos nuestros análisis anteriores veremos que ese valor no parece corresponderse con el valor de transacción anterior que habíamos encontrado anteriormente. Eso es por qué, de nuevo, el valor esta en Little-Endian. Si le damos la vuelta al endianness del valor obtenemos 67760645884791eefdc018e5ee843298d2e76aa6bdc7f2f59381363c3486db29.

Lo siguiente es extraer el índice de la salida de la transacción anterior que nuestra entrada esta intentando gastar (prev_out_index). Esto son 4 bytes, u 8 caracteres hexadecimales.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

El valor corresponde a 01000000, que como ya hemos visto con el número de versión, corresponde a 1 en decimal.

El siguiente campo a extraer es la longitud del script de entrada. Dado que diferentes transacciones pueden tener diferentes tipos de condiciones de gastado, es necesario codificar la longitud del script antes del propio script para saber que cantidad de bytes debemos leer al decodificar la transacción. Este campo también es de longitud variable, pero dado que el script en si ocupa menos de 255 bytes, la longitud cabe en un único byte (tendréis que hacer un voto de confianza aquí, ya que de otro modo, sin saber la longitud del script, ni saber como funciona la codificación VARINT, no hay forma de saber porque extraemos un único byte).

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

El valor extraído es 6b, que corresponde a 107 en decimal. Eso implica que el siguiente valor a extraer (scriptSig) ocupa 107 bytes (o 214 caracteres).

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

Por lo tanto, scriptSig corresponde a 483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314. Si volvéis hacia atrás para revisar el análisis que habíamos hecho con bitcoind de esta misma transacción, veréis que este valor es exactamente el mismo que obteníamos en el campo hex de scriptSig.

Vamos a diseccionar un poco más el valor obtenido. Para ello necesitamos entender un par de cosas:

  • Cualquier campo que no tiene una longitud fija dentro de la transacción siempre va prefijado por su longitud. De esta forma sabemos exactamente cuándo parar de leer. Ya hemos visto esto en campos anteriores, como es el caso del número de entradas o la longitud del script de entrada.
  • Las firmas digitales en bitcoin se codifican siguiendo el formato DER, que utiliza la codificación ASN.1.

Sin entrar en gran detalle en la codificación ASN.1, nos bastará con entender la siguiente imagen para poder decodificar el script.

Estructura de una firma ECDSA en Bitcoin

La estructura empieza con 30 que nos indica que lo que sigue es una secuencia de ASN.1. El siguiente byte, len(z), contiene la longitud de la firma, z. A continuación tenemos 02, que nos indica que el siguiente valor es un valor entero. len(r), de 1 byte, nos define la longitud del primer componente de la firma, r. Ese primer componente tendrá una longitud variable, que normalmente acostumbra a estar entre 31-33 bytes, pero puede ser inferior. Seguidamente tenemos de nuevo 02, definiendo un nuevo valor entero, la longitud de ese nuevo valor, len(s), que corresponde con el segundo componente de la firma, y seguidamente s, que sigue las mismas reglas que r. Para acabar tendremos ht, que corresponde a hash type, y nos define qué partes de la transacción se han utilizado al crear la firma.

Si esta parte se te ha atragantado un poco, no te preocupes, no es necesario entender cada parte para saber como extraer las partes del script.

483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314

Sabemos que 48 nos indica la longitud de la firma, (72 en decimal, 144 caracteres) por lo que 3045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db corresponde con la firma. El siguiente byte es hash type: 01, que nos indica que se ha utilizado ALL. El siguiente byte nos indica la longitud de la clave pública: 21, que corresponde a 33 en decimal (66 caracteres), por lo tanto 033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314 es la clave pública.

Si volvéis hacia atrás y comprobáis qué valores obtuvimos en el campo asm de scriptSig en la sección anterior, veréis que los valores coinciden.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

El último campo a extraer de nuestra única entrada es el número de secuencia, que tiene una longitud fija de 4 bytes. Este es ffffffff, como ya habíamos podido ver anteriormente.

Dado que esta transacción tenia una única entrada, el bloque de entradas ya ha sido completamente diseccionado. Ahora nos toca pasar al bloque de salidas.

El primer campo a extraer es el número de salidas. De forma análoga a el número de entradas, este valor es un VARINT, pero dado que la transición únicamente tiene dos salidas, sabemos que debemos extraer unicamente un byte.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

Como era de esperar, el valor extraído es 02, que se traduce a 2 en decimal, por lo tanto sabemos que tenemos dos salidas a extraer en este bloque.

Empezamos por el valor de nuestra primera salida. Sabemos que este campo tiene 8 bytes, por lo que extraemos los siguientes 16 caracteres.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

El valor extraído es 6a334c0000000000, o 00000000004c336a en BE, que se traduce, en decimal, a 4993898 sats, o 0.04993898 BTC.

Lo siguiente a extraer es la longitud del primer script de salida, de nuevo, dado que el script es relativamente corto, la longitud cabe en un único byte.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

Obtenemos 19, que corresponde a 25 en decimal, por lo que sabemos que el script ocupa los siguientes 50 caracteres.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

El script de salida, o scriptPubKey es 76a914bdf63990d6dc33d705b756e13dd135466c06b3b588ac, que corresponde con el valor hex obtenido en la sección anterior para la primera salida de la transacción.

Análogamente a lo que pasaba con las entradas, no existen delimitados de salidas (con saber el número de salidas y la longitud de cada campo nos basta), por lo que el siguiente campo a extraer corresponderá con el valor de la segunda salida (8 bytes).

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

El valor es fba8910000000000 (000000000091a8fb en BE) o 9545979 sats (0.09545979 BTC).

Los siguientes dos campos a extraer son la longitud del script de la segunda salida, y el script en sí. Aplicamos la misma técnica que anteriormente:

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

19 nos indica 25 bytes, igual que en la salida anterior.

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

Y el script de salida obtenido es 76a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000.

Si prestáis un poco de atención, veréis que los dos scripts tienen el mismo prefijo (76a9) y sufijo (88ac), y difieren en la parte central. Esto es debido a que los dos scripts son del mismo tipo (P2PKH) y la parte central corresponde al HASH160 de la clave pública, que se utiliza para derivar la dirección de Bitcoin. Fijaos que en ningún punto hemos encontrado las direcciones en sí, eso es debido a que estas no están codificadas como tal en la transacción. Hablaremos más sobre esto en un futuro, en el post sobre scripting.

Dado que nuestra transacción tenia únicamente dos salidas, solo nos resta un campo por extraer, el locktime (4 bytes).

010000000129db86343c368193f5f2c7bda66ae7d2983284eee518c0fdee91478845067667010000006b483045022100def2e8964b3159354794c8bbb1c513aa8d318b4e22c276682735015114dbce7f022001806dfe0989058f437187e1eb74ae5fb23e85dd33482eab51ab6870243c59db0121033735ce72e939d9042fac0c6e88455e137099638d52135cd0ab6fcb0b916a4314ffffffff026a334c00000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588acfba89100000000001976a9146d198f4b54717607787a2277f6e863ae6966b8fa88ac00000000

Podemos ver que no nos ha sobrado ni faltado ningún campo, lo que nos indica que el análisis ha sido correcto.

Conclusión

Si habéis llegado a este punto, os felicito, ya sabéis diseccionar transacciones a mano. Nos han faltado algunos conceptos avanzados para poder cubrir cualquier tipo de transición, pero os deberíais poder desenvolver con la gran mayoría… de las transaciones legacy.

Como algunos de vosotros habréis intuido, especialmente si habéis escuchado los pods L54 y L58, el formato de transición presentado corresponde a las transacciones originales de Bitcoin. Sin embargo, este formato fue actualizado con SegWit, y es ligeramente distinto para ese tipo de transacciones. Si estás interesado en saber como aplicar este tipo de análisis para SegWit deberás esperar al siguiente post al respecto, ya que este ya ha crecido mucho más de lo que me gustaría.

Para acabar, me gustaría que revisaras que campos pensabas que formaban parte de una transición ahora que sabes de qué están hechas. ¿Los has acertado todos?¿Ibas muy desencaminado?

Es muy habitual pensar que cosas como las direcciones de Bitcoin, el identificador de transición, las fees o incluso el valor de las entradas forman parte de la transición, pero como hemos podido ver, ese no es el caso. Gran parte de la información que atribuimos a las transacciones es en realidad metadatos generados por le nodo.

En cualquier caso, espero que este articulo te haya servido para profundizar en el protocolo Bitcoin y ampliar tus conocimientos. Si es así, nos vemos en el próximo.

Sobre el autor de este artículo

Bitcoin & Lightning dev. Building @eyeofsatoshi. PhD from UAB, ex-{UCL, UIUC}. Bitcoin and distributed networks.

¿Te ha sido útil esta madriguera? Puedes dejarme una propina vía Lightning network
Sergi Delgado

Paynyms (Samourai Wallet)

PM8TJMUpWEgiX6vW7qhvBXqxjXQGTt7TPd5nhmnzREnfJR1KqiTH3t8KrmScpVAs6w9jYpdjo9htHNAsoATGcMh2PEhkrku5MiY4KtXEqcpbQvvYcpGv
+coolunit1aa