Guide

Verifica una cabecera de Bitcoin con Python

Reconstruye la cabecera génesis, reproduce su hash y comprueba su objetivo codificado con una prueba local en Python y casos de error.

8 min de lecturaTransacciones
Verifica una cabecera de Bitcoin con Python

Verificar una cabecera sin confiar en un explorador

El hash de un bloque de Bitcoin puede reproducirse con un programa corto. Esta guía reconstruye la cabecera del bloque génesis de mainnet, aplica SHA-256 dos veces y comprueba el objetivo de prueba de trabajo codificado. Es un ejercicio sin conexión para desarrolladores que quieren comprender los bytes de un identificador de bloque antes de conectar su software a un nodo.

La referencia son los parámetros de cadena de Bitcoin Core 31.1, revisados el 21 de septiembre de 2026. Ese código fija la marca temporal, el nonce, la dificultad codificada, el hash esperado y la raíz de Merkle del bloque génesis. El ejercicio utiliza esas constantes públicas. No crea un monedero, no contacta con pares y no maneja dinero ni secretos.

El límite principal debe quedar claro desde el comienzo: reproducir el hash de una cabecera no valida un bloque completo ni la cadena actual. Una cabecera compromete las transacciones mediante un árbol de Merkle, pero este programa no recibe esas transacciones. Tampoco decide si el minero utilizó el objetivo exigido por la cadena anterior. Esa frontera debe mantenerse visible al adaptar el ejemplo.

Requisitos y resultado esperado

Se necesita una terminal local y Python 3 con los módulos estándar hashlib y struct. No hace falta instalar paquetes. El código siguiente se guarda como header_check.py en una carpeta desechable y se ejecuta con python3 header_check.py. Si el ejecutable se llama python, se utiliza ese nombre. La implementación se ejecutó para este artículo; la versión de Python y la salida constan en el manifiesto educativo.

El identificador esperado empieza por 000000000019d668 y termina en b60a8ce26f. Esos fragmentos no bastan: el programa comprueba el valor completo. Una ejecución correcta muestra una cabecera de 80 bytes, el hash completo, una comparación verdadera con el objetivo y una comparación falsa tras cambiar el nonce. También rechaza longitudes incorrectas y objetivos inválidos.

Una aserción fallida indica que el ejercicio no reprodujo el resultado esperado. Es evidencia para investigar, no algo que deba borrarse para terminar. Conviene conservar juntos el código y la salida al comparar entornos. No hay descargas de dependencias cuyos errores puedan ignorarse ni respuestas remotas en las que confiar.

Comprender la serialización antes de ejecutar

La referencia de cabeceras especifica un formato de 80 bytes. Este ejemplo escribe, en orden, versión, hash del bloque anterior, raíz de Merkle, marca temporal, objetivo compacto y nonce. La referencia anterior del génesis es cero porque esa cadena no tiene un bloque precedente.

El orden de bytes suele causar errores. Los campos numéricos se serializan en little-endian. La raíz de Merkle mostrada por las herramientas debe invertirse antes de insertarla. Después del hash se invierte el resumen para obtener el identificador habitual. Invertir los caracteres del texto sería incorrecto: el programa invierte bytes después de decodificar el hexadecimal.

struct.pack establece explícitamente el tamaño de los enteros. El prefijo < selecciona little-endian. i escribe la versión con signo; cada I escribe un campo sin signo. Comprobar la longitud permite detectar bytes de transacciones añadidos por accidente, un prefijo de longitud o un campo omitido. Un archivo de bloque completo no equivale a su cabecera.

Ejecutar el ejercicio completo sin conexión

import hashlib
import struct

EXPECTED = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
MERKLE = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
POW_LIMIT = int("00000000" + "f" * 56, 16)

def target_from_bits(bits):
    size = bits >> 24
    word = bits & 0x007fffff
    if size <= 3:
        word >>= 8 * (3 - size)
        target = word
    else:
        target = word << (8 * (size - 3))
    negative = word != 0 and bool(bits & 0x00800000)
    overflow = word != 0 and (size > 34 or (word > 0xff and size > 33)
                             or (word > 0xffff and size > 32))
    if negative or overflow or target == 0 or target > POW_LIMIT:
        raise ValueError("invalid mainnet proof-of-work target")
    return target

def inspect(header):
    if len(header) != 80:
        raise ValueError("expected exactly 80 header bytes")
    digest = hashlib.sha256(hashlib.sha256(header).digest()).digest()
    bits = struct.unpack_from("<I", header, 72)[0]
    return digest[::-1].hex(), int.from_bytes(digest, "little") <= target_from_bits(bits)

header = (struct.pack("<i", 1) + bytes(32) + bytes.fromhex(MERKLE)[::-1]
          + struct.pack("<III", 1231006505, 0x1d00ffff, 2083236893))
block_hash, satisfies = inspect(header)
assert block_hash == EXPECTED and satisfies
print("header_bytes:", len(header))
print("block_hash:", block_hash)
print("meets_encoded_target:", satisfies)

changed = bytearray(header)
changed[76] ^= 1
changed_hash, changed_satisfies = inspect(bytes(changed))
assert changed_hash != EXPECTED and not changed_satisfies
print("changed_nonce_meets_target:", changed_satisfies)

for bad_header in (header[:-1], header + b"\x00"):
    try:
        inspect(bad_header)
    except ValueError:
        pass
    else:
        raise AssertionError("length control did not reject")
for bad_bits in (0, 0x1d80ffff, 0x23000001, 0x1e00ffff):
    try:
        target_from_bits(bad_bits)
    except ValueError:
        pass
    else:
        raise AssertionError("target control did not reject")
print("negative_controls:", 7)

Desde la carpeta que contiene el archivo:

python3 header_check.py

Las dos llamadas a SHA-256 trabajan sobre datos binarios. El resumen interior pasa directamente al segundo hash; aplicar el segundo hash al texto hexadecimal produce otro resultado. La distinción también importa al traducir el ejemplo a otro lenguaje. Si una biblioteca devuelve una cadena hexadecimal, debe decodificarse antes de aplicar el segundo hash.

El programa no mina. Comprueba un nonce conocido contra una cabecera histórica conocida. El caso con el nonce modificado es un control negativo seleccionado y ejecutado, no una afirmación de que cualquier otro nonce deba fallar. Otro podría producir un hash por debajo del objetivo. Por eso se comprueba el resultado real y no se deduce del mero cambio de un campo.

Decodificar el objetivo conservando las comprobaciones

El campo nBits representa el objetivo en formato compacto. El byte superior determina la escala; la parte inferior contiene la magnitud y un bit de signo. target_from_bits sigue la decodificación de números compactos de Core y rechaza objetivos negativos, nulos, desbordados o superiores al límite.

Esas rutas de rechazo importan. Los enteros de Python crecen más allá del tamaño fijo utilizado por Core. Un simple desplazamiento aceptaría valores que la implementación de consenso rechaza. La comprobación explícita de desbordamiento evita que una comodidad del lenguaje cambie la regla demostrada. El límite de prueba de trabajo de mainnet se comprueba por separado de la comparación entre hash y objetivo.

La implementación de prueba de trabajo de Core compara el hash con un objetivo validado. Aquí el resumen se interpreta como un entero little-endian. No se reconvierte el hash mostrado mediante una convención incierta. La igualdad está permitida: la condición es menor o igual al objetivo.

El objetivo sigue procediendo de la propia cabecera. Un validador de red también debe determinar el objetivo exigido por el contexto de cadena. Aceptar la dificultad elegida por una cabecera sin ese contexto permitiría solicitar una prueba más fácil. El ejercicio termina deliberadamente antes de ese problema mayor.

Resolver diferencias en el resultado

Si el hash no coincide, primero se revisa la inversión de la raíz de Merkle y después la marca temporal y el nonce en el código fijado. No debe invertirse la cabecera completa. El hash interior debe usar .digest(), no .hexdigest(). Conviene imprimir la longitud antes de cambiar cualquier lógica relacionada con consenso.

Si Python muestra un error de sintaxis o sangría, hay que comprobar que el archivo contiene únicamente el código. Los delimitadores Markdown no forman parte de un programa Python. Si falta el ejecutable, se instala Python mediante el canal habitual de confianza del sistema operativo. Esta guía no requiere ejecutar un instalador descargado desde una página ajena.

Si un control negativo pasa inesperadamente, se conserva la salida y se revisa la entrada exacta. Las longitudes incorrectas deben rechazarse antes del hash. Los objetivos inválidos deben rechazarse al decodificar. Distinguir esas rutas permite saber si el programa detectó la condición prevista o falló por otro motivo.

Qué demuestra y qué queda fuera

El caso correcto demuestra que estas constantes públicas del génesis producen la cabecera esperada, reproducen su hash y satisfacen el objetivo codificado. También ejercita determinadas rutas de error. No cubre todas las codificaciones compactas, no establece límites de rendimiento, no valida transacciones y no reproduce todo el motor de consenso de Core.

Un nodo completo comprueba mucho más: contexto anterior, reglas de transacciones y la cadena que acepta. Un explorador sirve para consultar resultados, pero ni una captura ni este programa sustituyen esa validación. El ejercicio permite comprender y probar una frontera de serialización en trabajo desechable. Las decisiones de aceptación en producción corresponden a software de consenso mantenido.

Etiquetado

Boletín

Bitcoin, sin ruido

Qué ha pasado en Bitcoin, qué cambia de verdad, y las fuentes para que puedas comprobarnos. Un número cada vez, directo a tu correo.

  • Un correo por número, nunca una secuencia automática
  • Sin píxeles de seguimiento y sin compartir direcciones
  • Baja desde cualquier número con un clic

Recibe el próximo número

Un correo por número, sin píxeles de seguimiento, y te puedes dar de baja desde cualquiera de ellos. No compartimos tu dirección. Política de privacidad