Resumen
Este documento presenta en detalle un análisis criptográfico sobre cada una de las partes de las que se compone un bloque de datos en el criptosistema definido por el protocolo Bitcoin1. Se tratarán algunos aspectos importantes como los diferentes datos, tipos, formatos y la distribución de los mismos en la estructura del bloque, así como las operaciones lógico matemáticas de la función criptográfica SHA-2562 que se emplean con el fin de generar un hash resultante adecuado, es decir, un resultado que cumpla las características definidas en el protocolo Bitcoin. Finalmente este hash representará dicho bloque en lo que se conoce como la cadena de bloques, o como el propio creador Satoshi Nakamoto lo llamaba originalmente, timechain3. Todos los datos utilizados en los ejemplos, así como en los cálculos, son datos reales pertenecientes al bloque número #286819 de la cadena de bloques de Bitcoin en la red principal mainnet. No existe ninguna razón especial en la elección de este bloque, es uno al azar. El lector puede consultar los datos de este o cualquier otro bloque, bien en la cadena de bloques descargada en su computadora local o bien en un explorador de bloques online4, y realizar los mismos cálculos en todos los casos, obviamente obteniendo resultados diferentes.
Javier Domínguez Gómez
Cryptographer & Software Engineer
Miembro y hacktivista de la Free Software Foundation y la Electronic Frontier Foundation, desde donde se promueve la privacidad y se lucha por los derechos de los usuarios de plataformas digitales. Ingeniería de software y programador de bajo y medio nivel en Assembly, C y C++, con predilección por los sistemas GNU y Unix, los sistemas digitales, la criptografía y steganografía. Colabora como profesor (teaching fellow) del master en Big Data de la universidad Complutense de Madrid. Radioaficionado, apasionado de las comunicaciones y la Seguridad Operacional (OPSEC).
1. Introducción
1.1. Formatos en el texto
A lo largo de este documento el lector encontrará algunas palabras o bloques de texto con diferentes formatos o tipografía. Se mostrarán en letra cursiva las palabras en inglés o que hagan referencia a algún término técnico. Resaltadas o en negrita aquellas palabras que describen una propiedad, una variable o un nombre de fichero, y finalmente con tipografía courier aquellos datos que representen un valor hexadecimal o base 16, o fragmentos de código fuente en diferentes lenguajes de programación.
1.2. Objetivo
Este documento presenta en detalle un análisis criptográfico sobre cada una de las partes de las que se compone un bloque de datos en el criptosistema definido por el protocolo Bitcoin[1]. Se tratarán algunos aspectos importantes como los diferentes datos, tipos, formatos y la distribución de los mismos en la estructura del bloque, así como las operaciones lógico matemáticas de la función criptográfica SHA-256 que se emplean con el fin de generar un hash resultante adecuado, es decir, un resultado que cumpla las características definidas en el protocolo Bitcoin. Finalmente este hash representará dicho bloque en lo que se conoce como la cadena de bloques, o como el propio creador Satoshi Nakamoto lo llamaba originalmente, timechain[2]. Todos los datos utilizados en los ejemplos, así como en los cálculos, son datos reales pertenecientes al bloque número #286819 de la cadena de bloques de Bitcoin en la red principal mainnet. No existe ninguna razón especial en la elección de este bloque, es uno al azar. El lector puede consultar los datos de este o cualquier otro bloque, bien en la cadena de bloques descargada en su computadora local o bien en un explorador de bloques online, y realizar los mismos cálculos en todos los casos, obviamente obteniendo resultados diferentes.
1.3. A quién va dirigido
El documento va dirigido a principalmente a lectores familiarizados con la criptología en cualquiera de sus dos ramas: la criptografía y el criptoanálisis. También va dirigido a estudiantes de ciencias de la computación o matemáticas, en general a lectores que todavía no se han adentrado en el campo de del análisis forense criptográfico sobre la información que contiene un bloque de la cadena de bloques de Bitcoin, pero que quieren comprender su funcionamiento al más bajo nivel, a nivel de bits. El texto trata de facilitar al lector la información sobre los métodos empleados para obtener un hash que cumpla una serie de requisitos según el criptosistema planteado por el protocolo Bitcoin. Para ello será necesario tener de antemano unos conocimientos mínimos sobre matemática discreta, álgebra de Boole[3], conversión de datos en diferentes bases como binario, decimal y hexadecimal, y comprender cómo funcionan algunas operaciones lógicas, módulo, rotativas y de desplazamiento de bits.
2. Estructura de datos de un bloque
Cuando se hace referencia a un bloque en realidad se está haciendo referencia a un conjunto de datos ordenados en base a unas normas, que en el caso que nos atañe se definen cuando se desarrolla el protocolo Bitcoin, creando así una estructura de datos ordenada y segmentada de forma que sea sencillo identificar cada una de las partes. Los datos se ordenan en 5 secciones principales. La primera sección se reserva para un dato denominado magicId, un dato numérico de 4 bytes y la segunda sección para almacenar el dato size, al igual que el anterior también es un dato numérico de 4 bytes, este representará el tamaño final del bloque. En los siguientes puntos se explicará en detalle cada uno de los datos. Estos dos segmentos juntos se pueden usar como el delimitador que separa un bloque de otro en la cadena, pues los bytes que conforman los datos de los bloques se representan concatenados uno a continuación de otro. La siguiente imagen representa la estructura de un bloque en el que se muestra resaltado en color verde el segmento que alberga el dato magicId y en amarillo el segmento que alberga el dato size, ambos seguidos por el resto de datos del bloque, la zona de color gris punteada.
El tercer segmento tiene un espacio de memoria reservado de 80 bytes para almacenar los datos de la cabecera o header. El cuarto segmento almacenará un dato denominado transactionCount que representa el número de transacciones que contiene el bloque, será un número de tipo entero sin signo y el tamaño puede variar dependiendo del número de transacciones en cada bloque. Por último, el quinto segmento se reserva para almacenar todos los datos de los que se componen cada una de las transacciones, esta sección llamada transaction data tendrá un tamaño variable que se corresponde con el tamaño disponible o restante hasta completar el bloque. A continuación una representación gráfica de los 5 segmentos de datos de los que se compone cada bloque.
El segmento header a su vez se compone de 6 datos que son versionNumber, previousBlockHash, merkleRoot, timeStamp targetDifficulty o nonce. Todos los datos del bloque tienen sus propios atributos, como puede ser el tipo de dato o tipo primitivo, un nombre, el tamaño que ocupa en memoria y el formato en el que este se representará finalmente. En la siguiente tabla se categorizan los datos más significativos y a continuación se explican en detalle.
1. magicID: En la red de Bitcoin se establecen conexiones entre los nodos con la finalidad de establecer una comunicación para el envío y recepción de datos o mensajes. Los mensajes se envían mediante un canal en el que según van llegando se van concatenando uno detrás de otro. Así pues, cuando se desarrolló el protocolo Bitcoin se vio conveniente añadir un prefijo en cada mensaje anteponiendo este dato de 4 bytes y así poder identificar fácilmente no solo la red de Bitcoin en la que se generan, si no también dónde empieza y termina cada mensaje que circula entre los diferentes nodos de la red. La siguiente tabla muestra los diferentes valores que puede tener la variable magicID dependiendo de la red.
Red | magicID |
---|---|
Mainnet | 0xf9beb4d9 |
Testnet | 0xfabfb5da |
Testnet3 | 0x0b110907 |
Namecoin | 0xf9beb4fe |
Regtest | 0xfabfb5da |
Se decidió establecer estos valores tan específicos dada la improbabilidad de que los caracteres ASCII que representan se encuentren en un mensa- je estándar, tal y como se indica en el archivo chainparams.cpp[4] que lo implementa en el código de Bitcoin.
2. size: En un dato numérico hexadecimal de 4 bytes de valor variable que representa la longitud en bytes del bloque actual. Está codificado en formato Little-endian. Por ejemplo, un bloque con tamaño 152.509 KB tendrá el siguiente valor:
15250910 = 0x000253BD ⇒ 0xBD530200
3. versionNumber: Este dato hexadecimal puede cambiar de valor cuando se actualiza el software y cambia el número de la versión del protocolo. Tiene un tamaño de 4 bytes y se codifica en formato Little-endian, por ejemplo:
210 = 0x00000002 ⇒ 0x02000000
4. previousBlockHash: Se trata del hash o digest resultante del bloque anterior tras aplicar las funciones criptográficas utilizando los datos de la cabecera de dicho bloque. Tiene una longitud de 256 bits o 32 bytes codificado en formato Big-endian. Tal y como se explica al inicio del este documento el ejemplo en todo caso toma los datos correspondientes al bloque #286819, así pues, el valor de este dato ha de ser el hash resultante del bloque anterior, que en este caso es el bloque #286818[5].
000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717
5. merkleRoot: Cada vez que una nueva transacción es aceptada el valor de esta variable se modifica. Se trata de un hash hexadecimal de 32 bytes codificado en formato Big-endian. Para obtener el hash final, también llamado hash root se han de concatenar los datos de las transacciones ubicados en los nodos hoja del árbol, en grupos de dos. De ese modo, por cada dos transacciones se obtiene un hash nuevo que será incluido en un nuevo vector que repetirá la acción hasta llegar al hash root.
Cada uno de los vectores v = ( v1, v2, . . . , vn−1, vn) irá reduciendo el número de nodos o elementos mediante la siguiente función recursiva por intervalos o algoritmo de complejidad O(n).
A continuación un ejemplo escrito en lenguaje C++ que implementa dicho algoritmo[8]:
6. timeStamp: Se trata de un número entero sin signo de 4 bytes también llamado Epoch o Tiempo Unix. Representa el número de segundos que han transcurrido desde el 1 de enero de 1970 a las 00:00:00. Se codifica en formato Little-endian.
Un ejemplo de cómo se puede calcular este dato es mediante el siguiente código escrito en lenguaje C. Nótese que las horas se procesan como GMT+1.
7. targetDifficulty: A este dato también se le llama simplemente target o “Bits” si se hace referencia al empaquetado de datos en el bloque. Se trata de un número entero de 256 bits representado como un número decimal muy grande, tanto que abarcaría el rango de números existentes entre 0 y 2256 — 1. El siguiente número sería el valor hexadecimal máximo que podría tomar la variable targetDifficulty.
En el bloque se almacena como un número decimal de coma flotante truncando, por ejemplo, el valor hexadecimal anterior quedaría representado de la siguiente forma.
La dificultad es el resultado de dividir el valor máximo entre el valor actual de la variable targetDifficulty, tal y como se muestra en la siguiente fórmula.
A continuación un fragmento de código escrito en lenguaje C++ que realiza el cálculo y que se puede emplear para hallar la dificultad de minado.
Hay que tener en cuenta el valor de la variable targetDifficulty en cada caso, por ejemplo en el bloque #286819 dicho valor es 0x19015f53. Es una de las variables más importantes a tener en cuenta a la hora de obtener el hash adecuado del bloque antes de incorporarlo a la cadena como un bloque válido. Así pues la prueba de trabajo o Proof of Work tendrá mayor o menor dificultad. En el protocolo de Bitcoin se define una regla que dice que el hash del bloque ha de ser un número menor o igual al valor de esta variable targetDifficulty en ese momento. De modo que si el hash obtenido como candidato a generar un bloque fuese un número menor o igual al de targetDifficulty habría posibilidades para que el hash candidato sea válido para generar un nuevo bloque, aunque de forma adicional se han de cumplir otras condiciones. Por el contrario, si el hash obtenido como candidato fuera un número mayor que el valor de esta variable targetDifficulty, este quedaría desestimado, entonces habría que incrementar el valor de la variable nonce y repetir todo el proceso para generar un hash nuevo. El siguiente gráfico muestra la variación de la dificultad a lo largo del tiempo.
El valor de la variable targetDifficulty se modifica automáticamente una vez se han generado 2016 bloques, esto sucede aproximadamente cada dos semanas. El nuevo valor se obtiene mediante un cálculo que realizan todos los clientes Bitcoin de la red en el que toman el tiempo real que ha llevado generar los 2016 bloques y se obtiene la diferencia porcentual respecto al número de bloques que se esperaba haber calculado en el periodo de dos semanas. Cuanto menor sea el valor de la variable targetDifficulty más aumentará la dificultad para hallar un hash válido para el bloque.
8. nonce: Se trata de un número entero sin signo aleatorio con una longitud de 32 bits o 4 bytes codificado en formato Little-endian. Es el dato que ha de cambiarse tras un intento fallido por encontrar el hash del bloque adecuado, de modo que al incrementarlo se han de realizar de nuevo todos los cálculos de la función SHA-256 teniendo en cuenta el nuevo valor de esta variable. Siguiendo con el ejemplo del bloque #286819, este se incorporó a la cadena de bloques con un valor decimal o base 10 para la variable nonce de 856192328, lo que indica casi con total seguridad que se tuvieron que realizar bastantes intentos.
9. transactionCount: En el caso de transactionCount el tipo de dato es un entero sin signo de longitud variable. Como el propio nombre indica, dependiendo del número de transacciones que han sido procesadas tendrá un valor numérico u otro.
*Cuando se le asigna por valor un entero muy grande se codifica en formatoLittle-endian.
2.1. Datos de la cabecera o header
La siguiente imagen es una captura de pantalla de los datos del bloque #286819 visualizados en un explorador de bloques. Se han resaltado los datos que forman parte de la cabecera o header del bloque.
3. Construcción de la cadena de entrada M
Siguiendo con el ejemplo del bloque #286819 de la cadena de bloques de Bitcoin, dicho bloque está representado mediante el siguiente hash.
0000000000000000e067a478024addfecdc93628978aa52d91fabd4292982a50
A continuación los datos correspondientes a la cabecera o header del bloque:
Version: | 2 |
Prev. Block: | 000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717 |
Merkle root: | 871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a |
Timestamp: | 2014-02-20 04:57:25 (Epoch timestamp: 1392872245) |
Bits: | 419520339 |
Nonce: | 856192328 |
En este punto hay que pasar los datos en formato decimal a formato hexadecimal, de modo que quedarían de la siguiente manera:
Version: | 00000002 |
Prev. Block: | 000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717 |
Merkle root: | 871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a |
Timestamp: | 53058B35 |
Bits: | 19015F53 |
Nonce: | 33087548 |
Ahora hay que reorganizar los datos en formato Little-Endian:
Finalmente se concatenan uno a continuación de otro, empezando por version, seguido del hash del bloque anterior, merkle root, timestamp, bits y nonce, formando una cadena de entrada M de 160 carácteres hexadecimales con un tamaño total de 640 bits.
Se segmenta la cadena de entrada M en bloques de 32 bits:
De este modo se obtiene el mensaje de entrada M. Se ha de reservar este dato para utilizarlo más adelante en la función SHA-256 como input en la primera ronda.
3.1. Longitud de la cadena de entrada M
Una vez que se ha obtenido la cadena de entrada M en el punto anterior es necesario calcular la longitud de la misma en formato hexadecimal o base 16, es decir, 640 bits que se representan con el valor 280 en hexadecimal.
|M | = 280 (640 bits del mensaje original en hexadecimal)
Este dato se ha de reservar para el siguiente punto, ya que será necesario para completar los registros W14 y W15 de la variable Wt solo en la segunda y tercera ejecución de la función SHA-256.
3.2. La variable Wt
Tal y como se describe en el punto 3 del documento “Criptografía aplicada: Función SHA-256“12, la variable Wt es un vector de 64 elementos que contiene palabras hexadecimales de 32 bits. Tiene un tamaño o longitud de 2048 bits (256 bytes) y se obtiene mediante la siguiente función recursiva definida por intervalos.
En la anterior función recursiva definida por intervalos, el primer intervalo es el que abarca los 16 primeros registros, o sea desde W0 hasta W15. El segundo intervalo es el esquema de los 48 registros restantes, es decir, desde W16 hasta W63.
Las funciones σ0 y σ1 realizan las siguientes operaciones lógicas de compresión con cada uno de los bits de la palabra almacenada en el segmento de Wt que procesan.
σ0(x) = ROT R7(x) ⊕ ROT R18(x) ⊕ SHR3(x)
σ1(x) = ROT R17(x) ⊕ ROT R19(x) ⊕ SHR10(x)
Para hallar el valor de cualquiera de los segmentos que van desde W16 hasta W63 se han de realizar las siguientes operaciones, donde i tiene por valor el valor de t en el segmento que se quiere calcular de Wt. Por ejemplo, para W16:
σ1(Wi−2) + Wi−7 + σ0(Wi−15) + Wi−16
⇓
σ1(W16−2) + W16−7 + σ0(W16−15) + W16−16
⇓
σ1(W14) + W9 + σ0(W1) + W0
En este ejemplo los segmentos W14, W9, W1 y W0 de la primera ronda (Ronda#0 ) tienen asignados los siguientes valores:
W14 = 0xb2b92b3a W9 = 0x8a97295a
W1 = 0x17975b97 W0 = 0x02000000
A continuación se muestra una representación gráfica la operación lógica de compresión σ1, que aplica sobre el valor del segmento W14.
Tras el cálculo de σ1(W14) se obtiene la palabra hexadecimal de 32 bits 0xb0d6a141. Se ha de reservar este valor para calcular el resultado final. También se muestra la representación gráfica la operación lógica de compresión σ0, que aplica sobre el valor del segmento W1.
Tras el cálculo de σ0(W1) se obtiene la palabra hexadecimal de 32 bits fa380020. Una vez realizados los cálculos necesarios para obtener el valor de σ1(W14) y σ0(W1) finalmente se ha de realizar la siguiente operación final.
De este modo se obtiene el valor que se almacenará en el segmento W16 del vector Wt. Será necesario repetir el mismo proceso por cada uno de los segmentos hasta llegar a W63.
Todos los cálculos anteriores también aplican a las reglas criptográficas de los bloques en el protocolo Bitcoin, solo que hay que tener en cuenta que la cadena de entrada M en cada bloque tiene una longitud superior a 512 bits, lo que implica utilizar la función hash SHA-256 tres veces, una por cada ronda, y el valor de la longitud de M solo se aplicará en el esquema de relleno de la segunda y tercera ronda. En la primera ronda (Ronda #0 ) se rellenan los primeros 16 segmentos de la variable Wt con todos los bytes en hexadecimal que quepan, es decir, los primeros 512 bits de M , rellenando también los segmentos W14 y W15.
Los 128 bits restantes de la cadena de entrada M que no caben en esta primera ronda se reservan para los primeros 4 registros de Wt de la segunda ronda. Tras la segunda ronda (Ronda #1 ) se obtiene un digest de 256 bits que se utiliza para rellenar los primeros 8 segmentos de Wt en la tercera y última ronda (Ronda #2 ). La siguiente figura se detalla el contenido de los primeros 16 registros de la variable Wt en cada una de las tres veces que se ejecutan las 64 iteraciones de la función SHA-256 para obtener el digest en cada caso.
En los siguientes puntos se detalla el esquema de relleno al completo en cada una de las rondas.
3.3. Primera ronda SHA-256: Ronda 0
La función hash SHA-256 realiza 64 ciclos criptográficos en los que procesará una serie de cálculos en los que tiene en cuenta por un lado las 8 palabras hexadecimales de 32 bits asignadas como valor a las variables A, B, C, D, E, F , G y H, y por otro lado la variable Wt, que es un vector de 64 elementos ordenados por intervalos. En la primera ronda criptográfica se han iniciar los valores de las variables A0, B0, C0, D0, E0, F0, G0 y H0 con los valores estándar de la función hash SHA- 256 en su primera iteración, es decir, los 32 primeros bits en hexadecimal o base 16 de la parte fraccionaria de las raíces cuadradas de los primeros 8 números primos.
En una función SHA-256, cuando el tamaño de la cadena de entrada M es igual o mayor que 512 bits se han de rellenar los primeros 16 elementos del vector Wt con los primeros 512 bits de la cadena principal de entrada M , incluidos los elementos W14 y W15, tal y como se muestra en la imagen. El resto de la cadena M se ha de reservar para la segunda ejecución de la función SHA-256 que se explicará en el siguiente punto. Teniendo la cadena M de entrada, las 8 palabrasiniciales, que irán cambiando en cada una de las 64 iteraciones, y los 64 elementos del vector Wt se puede comenzar a realizar las operaciones de la función SHA-256. El objetivo en esta primera ronda es obtener el primer hash, necesario en la segunda ronda.
Cuando se hayan realizado las 64 iteraciones SHA-256 con los datos anteriores se ha de realizar una última operación en la que se ha de tomar cada palabra A, B, C, D, E, F , G y H de la primera iteración y realizar una operación mod 232 con su homóloga de la última iteración, véase el ejemplo.
Finalmente se concatenan los valores de los 8 resultados en esta ronda 0 obteniendo el siguiente digest resultante:
dc6a3b8d0c69421acb1a5434e536f7d5c3c1b9e44cbb9b8f95f0172efc48d2df
Se reserva este dato para realizar la segunda ronda SHA-256 en el siguiente punto.
3.4. Segunda ronda SHA-256: Ronda 1
En la segunda ronda se han volver a realizar los 64 ciclos criptográficos que realiza SHA-256 teniendo en cuenta que el valor de las 8 palabras iniciales A, B, C, D, E, F , G y H en la primera iteración tendrán por valor inicial cada uno de los segmentos de 32 bits correspondientes al hash resultante de la primera ronda.
En cuanto a los primeros 16 registros de la variable Wt en esta segunda ronda se utilizarán primero los bits del mensaje M original de 640 bits que no cabían en la ronda anterior.
dc141787 + 358b0553 + 535f0119 + 48750833
A continuación hay que tomar un bit que represente el número 1 decimal o base 10, es decir 00000001, este se desplaza al bit más alto del byte, con lo que se obtiene 10000000 y finalmente se calcula el valor hexadecimal, que es 80.
100000002 = 8016
Independientemente de la longitud de cadena hexadecimal de la palabra de entrada, se añade 80 por la derecha.
486f6c61 + 206d756e + 646f + 80
Ahora hay que añadir a la cadena una cantidad de bits con valor 0 hasta llegar a 448 bits en total, que es la longitud que abarca todos los intervalos que van desde W0 hasta W13. Para terminar de completar los primeros 16 segmentos de Wt solo queda rellenar los últimos dos bloques de 32 bits W14 y W15 con la longitud del mensaje de entrada |M| en hexadecimal, con tantos ceros por la izquierda como sean necesario para alcanzar 64 bits de longitud, en este caso es 280. Los valores que irán en los segmentos desde W16 hasta W63 se obtienen siguiendo las pautas explicadas en el punto 6 de este documento. Al igual que en la primera ronda, cuando se hayan realizado las 64 iteraciones SHA-256 con los datos anteriores se ha de realizar una última operación en la que se ha de tomar cada palabra A, B, C, D, E, F , G y H de la primera iteración y realizar una operación mod 232 con su homóloga de la última iteración, véase el ejemplo.
Finalmente se concatenan los valores de los 8 resultados en esta ronda 1 obteniendo el siguiente digest resultante:
7c122b86287a3ef7eac247e0ad637091ccfecbf85f6213030d9c1f895515d9e6
Se reserva este dato para realizar la tercera ronda SHA-256 en el siguiente punto.
3.5. Tercera ronda SHA-256: Ronda 2
En esta tercera ronda criptográfica (Ronda #2 ) se ha de utilizar como mensaje de entrada M el hash obtenido en la ronda anterior (Ronda #1 ). Al igual que en la ronda anterior hay que tomar un bit que represente el número 1 decimal o base 10, es decir 00000001, este se desplaza al bit más alto del byte, con lo que se obtiene 10000000 y finalmente se calcula el valor hexadecimal, que es 80.
100000002 = 8016
Independientemente de la longitud de cadena hexadecimal de la palabra de entrada, se añade 80 por la derecha.
Ahora hay que añadir a la cadena una cantidad de bits con valor 0 hasta llegar a 448 bits en total, que es la longitud que abarca todos los intervalos que van desde W0 hasta W13. Cuando se hayan realizado las 64 iteraciones SHA-256 con los datos anteriores se ha de realizar una última operación en la que se ha de tomar cada palabra A, B, C, D, E, F , G y H de la primera iteración y realizar una operación mod 232 con su homóloga de la última iteración, véase el ejemplo.
Finalmente se concatenan los valores de los 8 resultados en esta ronda 0 obteniendo el siguiente digest resultante:
502a989242bdfa912da58a972836c9cdfedd4a0278a467e00000000000000000
Para terminar, el último paso para obtener el hash definitivo de este bloque es convertir el digest resultante a formato Little-Endian, tal y como se muestra a continuación.
0000000000000000e067a478024addfecdc93628978aa52d91fabd4292982a50
Se puede comprobar que se cumplen los requisitos del protocolo Bitcoin para el cálculo del hash del bloque #286819 de la cadena de bloques de Bitcoin en la red principal mainnet. Los datos empleados en la realización de este documento se pueden comprobar con los de la cadena de bloques de Bitcoin desde en cualquier explorador de bloques13.
4. Almacenamiento en ficheros
Todos los datos de los bloques que ya han sido minados se escriben en ficheros binarios con el nombre blk*.dat. Estos archivos se almacenan en diferentes directorios dependiendo del sistema operativo que tenga la computadora donde se esté ejecutando el cliente Bitcoin Core, véase la siguiente tabla:
Sistema operativo | Directorio |
---|---|
GNU/Linux y Unix | ~/.bitcoin/blocks/ |
MacOS | ~/Library/Application Support/Bitcoin/blocks |
Windows XP | %APPDATA%nBitcoinnblocks |
Windows Vista/7/8 | C:nUsersnusernAppDatanRoamingnBitcoinnblocks |
4.1. Listado y lectura de archivos
En este ejemplo se ha empleado un sistema operativo 100 % libre[7] GNU/Linux, así pues hay que posicionarse en el directorio ~/.bitcoin/blocks/. Si se listan los archivos que contiene el directorio aparecerá una lista como la siguiente:
Entre todos estos archivos los que nos interesan son los que su nombre comienza por blk seguido de unos números y terminando con la extensión .dat. Al tratarse de archivos binarios no se puede leer su contenido con un editor de textos tradicional, será necesario utilizar un programa específico para tal caso, como por ejemplo el programa hexdump[8]. Una vez abierto cualquiera de los archivos, por ejemplo el primer archivo blk00000.dat, así es como se ven los datos que contiene.
Estos son los primeros bytes del primer bloque en el primer archivo de la cadena de bloques de Bitcon, también llamado bloque génesis. Este primer archivo no solo contiene los datos del primer bloque, hay muchos más, ordenados cronológicamente según se van minando y añadiendo a la cadena. Tal y como se explica al inicio del punto 2 de este documento, los datos de los bloques se van concatenando uno a continuación de otro.
4.2. Visualización de datos los datos en el bloque
Siguiendo con el ejemplo del bloque #286819, en condiciones normales, es decir, siempre y cuando a la cadena de bloques descargada no se le haya aplicado algún tipo de prune[9] o podado, los datos se encuentran en el archivo blk00116.dat. Todos los archivos comienzan con los datos de un bloque pero los datos del bloque que se quiere analizar no tienen por qué encontrarse en esa posición, es bastante probable que los datos comiencen muchos bytes más adelante o en un offset mucho mayor desde el inicio del archivo. En este caso concreto, los datos comienzan a partir del byte número 93725126, en el offset número 059621c6. En el siguiente ejemplo se ejecuta el programa hexdump con la opción -C para obtener una salida standard por pantalla, también con la opción -s seguido del número de desde el que se quiere empezar a visualizar datos, y la opción -n para indicar el número total de bytes que se quieren mostrar, en este caso con 288 bytes es suficiente.
En la columna izquierda se ve el offset correspondiente a cada línea, en la parte central los datos del bloque en formato hexadecimal y la columna de la derecha cada uno de los 16 de datos representados en caracteres ASCII[10]. Se ha resaltado en diferentes colores los diferentes segmentos de datos. En color rojo aparecen los 4 que representan el dato magicID, a continuación en color verde los 4 siguientes el dato size que representa el tamaño final de este bloque. El segmento que sigue en color turquesa son los 80 que representan el header. En color azul un único byte que representa el número de transacciones registradas en el bloque y finalmente en color morado se han resaltado todos los datos correspondientes a las transacciones del bloque. Los datos no terminan en el offset 059622e6, se ha truncado el bloque en ese punto para une mejor visualización en este documento, pues la cantidad de datos del bloque mostrada en este formato podría abarcar demasiado espacio.
Referencias
1. Nakamoto, S. (2019). Bitcoin: A peer-to-peer electronic cash system. Manubot. https://bitcoin.org/bitcoin.pdf. Último acceso: 2 de abril de 2020.
2. Bitcoin Forum. https://bitcointalk.org/index.php?topic=382374.0. Último acceso: 2 de abril de 2020.
3. Wikipedia. Boolean algebra. https://en.wikipedia.org/wiki/Boolean algebra. Último acceso: 2 de abril de 2020.
4. Github. Bitcoin. https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L99. Último acceso: 2 de abril de 2020.
5. Block stream. https://blockstream.info/block/000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717. Último acceso: 2 de abril de 2020.
6. GitHub. JavierDominguezGomez/Cryptography. https://github.com/JavierDominguezGomez/Cryptography/blob/master/MerkleTree/merkleTree.cpp. Último acceso: 2 de abril de 2020.
7. GNU. Distribuciones libres de GNU/Linux. https://www.gnu.org/distros/free-distros.es.html. Último acceso: 2 de abril de 2020.
8. FreeBSD. Manual Pages. https://www.freebsd.org/cgi/man.cgi?query=hexdump&sektion=1. Último acceso: 2 de abril de 2020.
9. Bitcoin. Running Bitcoin. https://en.bitcoin.it/wiki/Running Bitcoin#Command-line arguments. Último acceso: 2 de abril de 2020.
10. IEEE. ASCII Code Table. https://www.ieee.li/computer/ascii.htm. Último acceso: 2 de abril de 2020.