Cada bloque en la cadena de bloques bitcoin contiene un resumen de todas las transacciones en el bloque, utilizando un árbol merkle.
Un árbol merkle, también conocido como un árbol hash binario, es una estructura de datos que se usa para resumir y verificar de manera eficiente la integridad de grandes conjuntos de datos. Los árboles merkle son árboles binarios que contienen hashes criptográficos. El término "árbol" se usa en informática para describir una estructura de datos de ramificación, pero estos árboles por lo general aparecen al revés, con la "raíz" en la parte superior y las "hojas" en la parte inferior de un diagrama, como se verá en los ejemplos que siguen.
Figure 1. Bloques enlazados en una cadena, por referencia al hash de la cabecera del bloque anterior
Los árboles merkle se usan en bitcoin para resumir todas las transacciones en un bloque, produciendo una huella digital completa de todo el conjunto de transacciones, proporcionando un proceso muy eficiente para verificar si una transacción está incluida en un bloque. Un árbol merkle se construye mediante la ejecución de una función de hash en pares de nodos de forma recursiva hasta que solo queda un único hash, al que se le llama raíz o raíz merkle. El algoritmo de hash criptográfico utilizado en los árboles merkle de bitcoin es SHA256 aplicado dos veces, también conocido como doble-SHA256. Cuando se toman N elementos de datos, se hace hash de cada uno de ellos y se resumen en un árbol merkle, se puede comprobar si cualquier elemento de datos está incluido en el árbol con un máximo de 2*log~2~(N) cálculos, convirtiéndolo en una estructura de datos muy eficiente.
El árbol merkle se construye de abajo hacia arriba. En el siguiente ejemplo, comenzamos con cuatro transacciones, A, B, C y D, que forman la hojas del árbol Merkle, como se muestra en <<simple_merkle> >. Las transacciones no se almacenan en el árbol de Merkle; más bien, se hace hash de sus datos y el hash resultante se almacena en cada nodo hoja como HA, HB, HC, y HD:
H~A~ = SHA256(SHA256(Transacción A))
Después, los pares consecutivos de nodos hoja se resumen en un nodo padre, concatenando los dos hashes y haciendo hash de ese dato concatenado. Por ejemplo, para construir el nodo padre HAB, los dos valores hash de 32 bytes de los hijos se concatenan para crear una cadena de 64 bytes. Se hace entonces un doble-hash de esa cadena para producir el hash del nodo padre:
H~AB~ = SHA256(SHA256(H~A~ + H~B~))
El proceso continúa hasta que solo hay un nodo en la parte superior, el nodo conocido como la raíz Merkle. Ese hash de 32 bytes se almacena en la cabecera del bloque y resume todos los datos de las cuatro transacciones.
Figure 2. Calculando los nodos en un árbol merkle
Debido a que el árbol merkle es un árbol binario, se necesita un número par de nodos hoja. Si hay un número impar de transacciones para resumir, el último hash de transacción se duplicará para crear un número par de nodos hoja, también conocido como árbol equilibrado. Esto se muestra en <<merkle_tree_odd> >, donde se duplica la transacción C.
Figure 3. Duplicando un elemento de datos para alcanzar un número par de elementos de datos
El mismo método para la construcción de un árbol de cuatro transacciones se puede generalizar para construir árboles de cualquier tamaño. En bitcoin es común tener de varios cientos a más de mil transacciones en un solo bloque, que se resumen de la misma forma, produciendo solo 32 bytes de
datos en una única raíz merkle. En Un árbol merkle resumiendo muchos elementos de datos, verá un árbol construido a partir de 16 transacciones. Tenga en cuenta que, aunque la raíz se ve más grande que los nodos hoja en el diagrama, tiene exactamente el mismo tamaño, solo 32 bytes. Independientemente de si existe una transacción o cien mil transacciones en el bloque, la raíz merkle siempre los resume en 32 bytes.
Para demostrar que una transacción específica está incluida en un bloque, un nodo solo necesita producir log~2~(N) hashes de 32 bytes, elaborando una ruta de autenticación o ruta merkle que conecte la transacción específica a la raíz del árbol. Esto es especialmente importante a medida que el número de transacciones aumenta, porque el logaritmo en base-2 del número de transacciones aumenta mucho más lentamente. Esto permite que los nodos bitcoin produzcan eficientemente rutas de 10 ó 12 hashes (320-384 bytes), que pueden proporcionar la prueba de la existencia de una sola transacción entre más de mil transacciones en un bloque de un megabyte de tamaño.
Figure 4. Un árbol merkle resumiendo muchos elementos de datos
En Una ruta merkle utilizada para probar la inclusión de un elemento de datos, un nodo puede demostrar que una transacción K está incluida en el bloque mediante la producción de una ruta merkle que ocupa solo cuatro hashes de 32-bytes de largo (128 bytes en total). La ruta consta de los cuatro valores hash (señalados en azul en Una ruta merkle utilizada para probar la inclusión de un elemento de datos) HL, HIJ, HMNOP and HABCDEFGH. Con esos cuatro hashes suministrados a modo de ruta de autenticación, cualquier nodo puede demostrar que HK (marcado en verde en el diagrama) está incluido en la raíz merkle mediante el cálculo de cuatro hashes adicionales por pares HKL, HIJKL, H IJKLMNOP, y la raíz del árbol merkle (descrito en una línea de puntos en el diagrama).
Figure 5. Una ruta merkle utilizada para probar la inclusión de un elemento de datos
El código en Construyendo un árbol merkle demuestra el proceso de crear un árbol merkle desde el hash del nodo hoja hasta la raíz, utilizando la biblioteca libbitcoin para algunas funciones auxiliares.
Example 1. Construyendo un árbol merkle
#include <bitcoin/bitcoin.hpp>
bc::hash_digest create_merkle(bc::hash_list& merkle) {
// Stop if hash list is empty. if (merkle.empty())
return bc::null_hash; else if (merkle.size() == 1) return merkle[0];
// While there is more than 1 hash in the list, keep looping... while (merkle.size() > 1)
{
// If number of hashes is odd, duplicate last hash in the list. if (merkle.size() % 2 != 0)
merkle.push_back(merkle.back()); // List size is now even.
assert(merkle.size() % 2 == 0); // New hash list.
bc::hash_list new_merkle;
// Loop through hashes 2 at a time.
for (auto it = merkle.begin(); it != merkle.end(); it += 2) {
// Join both current hashes together (concatenate). bc::data_chunk concat_data(bc::hash_size * 2);
auto concat = bc::make_serializer(concat_data.begin()); concat.write_hash(*it);
concat.write_hash(*(it + 1));
assert(concat.iterator() == concat_data.end()); // Hash both of the hashes.
bc::hash_digest new_root = bc::bitcoin_hash(concat_data); // Add this to the new list.
new_merkle.push_back(new_root); }
// This is the new list. merkle = new_merkle;
// DEBUG output --- std::cout << "Current merkle hash list:" << std::endl; for (const auto& hash: merkle)
std::cout << " " << bc::encode_hex(hash) << std::endl; std::cout << std::endl;
// --- }
// Finally we end up with a single item. return merkle[0];
}
int main() {
// Replace these hashes with ones from a block to reproduce the same merkle root. bc::hash_list tx_hashes{{ bc::hash_literal("0000000000000000000000000000000000000000000000000000000000000000"), bc::hash_literal("0000000000000000000000000000000000000000000000000000000000000011"), bc::hash_literal("0000000000000000000000000000000000000000000000000000000000000022"), }};
const bc::hash_digest merkle_root = create_merkle(tx_hashes);
std::cout << "Result: " << bc::encode_hex(merkle_root) << std::endl; return 0;
}
Compilando y ejecutando el código de ejemplo merkle muestra el resultado de compilar y ejecutar el código merkle.
Example 2. Compilando y ejecutando el código de ejemplo merkle
$ # Compilar el código merkle.cpp
$ g++ -o merkle merkle.cpp $(pkg-config --cflags --libs libbitcoin) $ # Ejecutar el ejecutable merkle
$ ./merkle
Lista actual de hash merkle:
32650049a0418e4380db0af81788635d8b65424d397170b8499cdc28c4d27006 30861db96905c8dc8b99398ca1cd5bd5b84ac3264a4e1b3e65afa1bcee7540c4 Lista actual de hash merkle:
d47780c084bad3830bcdaf6eace035e4c6cbf646d103795d22104fb105014ba3 Result: d47780c084bad3830bcdaf6eace035e4c6cbf646d103795d22104fb105014ba3
La eficiencia de los árboles merkle se hace evidente a medida que aumenta la escala. Eficiencia de un árbol merkle muestra la cantidad de datos que necesitan intercambiarse como una ruta merkle para demostrar que una transacción está incluida en un bloque.
Table 3. Eficiencia de un árbol merkle
Número de
transacciones Tamaño aprox. delbloque tamaño de ruta (hashes) Tamaño de ruta (bytes) 16 transacciones 4 kilobytes 4 hashes 128 bytes
512 transacciones 128 kilobytes 9 hashes 288 bytes 2048 transacciones 512 kilobytes 11 hashes 352 bytes 65.535 transacciones 16 megabytes 16 hashes 512 bytes
Como se puede ver en la tabla, mientras que el tamaño de bloque aumenta rápidamente, de 4 KB con 16 transacciones a un tamaño de bloque de 16 MB para incluir a 65.535 transacciones, la ruta merkle requerida para demostrar la inclusión de una transacción aumenta mucho más lentamente, de 128 bytes a solo 512 bytes. Con árboles merkle, un nodo puede descargar solo las cabeceras de bloque (80 bytes por bloque) y aún así ser capaz de identificar la inclusión de una transacción en un bloque mediante la recuperación de una ruta merkle pequeña de un nodo completo, sin almacenar o transmitir la gran mayoría de la cadena de bloques, que puede ser de varios gigabytes de tamaño. Los nodos que no mantienen una cadena de bloques completa, llamados de verificación de pago simplificada (nodos SPV), usan rutas merkle para verificar las transacciones sin necesidad de descargar bloques completos.