Deep Dive

Cómo Bitcoin sostiene un intervalo de diez minutos por bloque

Cada 2.016 bloques, cada nodo recalcula el objetivo de minería a partir de dos marcas de tiempo y una división. Acá está la aritmética, el tope que la limita, el error de uno que nadie puede corregir y por qué un colapso de hashrate implica esperar.

10 min de lecturaDificultad
Cómo Bitcoin sostiene un intervalo de diez minutos por bloque

Nadie fija el ritmo de bloques de Bitcoin. No hay planificador ni ninguna señal que le informe a la red cuánto hardware está enchufado. Lo que hay es un número en cada cabecera de bloque, una regla para recalcularlo cada 2.016 bloques y las marcas de tiempo que los propios mineros escribieron. Este es ese mecanismo en los términos que usa el código, citando Bitcoin Core v31.1 en todo el texto. Para quien no se haya cruzado con proof of work, qué hacen los mineros es la versión sin el detalle de bytes.

Qué está buscando realmente un minero

Una cabecera de bloque son 80 bytes: versión, el hash de la cabecera anterior, la raíz merkle de las transacciones, una marca de tiempo, un campo de cuatro bytes llamado nBits y un nonce de 32 bits. Minar es hashear esa cabecera dos veces con SHA-256, leer el resultado como un número de 256 bits y preguntar si es igual o menor que el objetivo codificado en nBits. Si no lo es, el minero cambia algo (el nonce, el extra nonce de la coinbase, la marca de tiempo, el conjunto de transacciones) y vuelve a hashear. No hay crédito parcial ni forma de dirigir la búsqueda, que es por qué la cantidad de intentos es una magnitud medible y comprable.

La verificación en sí son cuatro líneas: CheckProofOfWorkImpl en src/pow.cpp deriva el objetivo a partir de nBits, compara y devuelve. nBits es una codificación compacta de un byte de exponente y tres de mantisa, así que el objetivo lleva apenas 24 bits de precisión. Un dial grueso, y el consenso no comprueba nada más sobre el trabajo.

La dificultad es un número de conveniencia

"Dificultad" no aparece en ninguna parte de esa verificación. Es una razón calculada para consumo humano, y GetDifficulty vive en la capa RPC y no en el código de consenso. Divide el objetivo de dificultad 1, que es lo que la forma compacta 0x1d00ffff decodifica de vuelta, por el objetivo actual.

Ese valor se parece pero no es igual al powLimit de mainnet, declarado en chainparams.cpp como 0x00000000ffffffff...ffff, porque la codificación compacta trunca la mantisa. Una dificultad de 127,48 billones, donde estaba la red el 20 de agosto de 2026 según mempool.space, significa que el objetivo en uso era esa cantidad de veces más chico que el techo. Ningún nodo guarda la dificultad como estado de consenso, y toda regla que sigue está escrita contra el objetivo.

El reajuste: cada 2.016 bloques, y como mucho por un factor de cuatro

Dos constantes fijan el ritmo, ambas en chainparams.cpp (líneas 96 a 98):

consensus.nPowTargetTimespan = 14 * 24 * 60 * 60;   // 1.209.600 segundos
consensus.nPowTargetSpacing  = 10 * 60;             // 600 segundos

El intervalo de reajuste no es una tercera constante. DifficultyAdjustmentInterval() es la primera dividida por la segunda: 2.016. Entre reajustes no se mueve nada: GetNextWorkRequired devuelve sin cambios el nBits del bloque anterior salvo que la altura en construcción sea múltiplo de 2.016, así que todos los bloques de un período se minan contra un objetivo idéntico.

En el límite de un período, el nodo mide cuánto tardó realmente el anterior y escala:

nuevo_objetivo = objetivo_anterior * nActualTimespan / nPowTargetTimespan

Más lento de lo esperado significa un nActualTimespan mayor, un objetivo mayor y bloques más fáciles. Más rápido significa lo contrario. Antes de esa multiplicación, el lapso medido se acota (src/pow.cpp):

if (nActualTimespan < params.nPowTargetTimespan/4)
    nActualTimespan = params.nPowTargetTimespan/4;
if (nActualTimespan > params.nPowTargetTimespan*4)
    nActualTimespan = params.nPowTargetTimespan*4;

Un reajuste puede mover el objetivo por un factor de cuatro y no más, en cualquiera de las dos direcciones. Esto se omite de forma rutinaria, y decide cómo se resuelve un shock. Supongamos que nueve décimas partes del hashrate desaparecen de un día para el otro. Los bloques tardarían diez veces más, así que el período correría unos 140 días en vez de 14. El tope limita la corrección a cuatro, lo que deja el intervalo en unos 25 minutos, todavía dos veces y media el objetivo. Recién el período siguiente, otros 35 días más o menos, termina el trabajo. La cota protege a la cadena de un lapso medido disparatado o manipulado, y lo paga haciendo que un colapso genuino necesite dos reajustes para absorberse.

El error de uno que no se puede corregir

Encontrar el inicio del período se ve así (src/pow.cpp):

// Go back by what we want to be 14 days worth of blocks
int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);

pindexLast es el último bloque del período que termina. Retroceder 2.015 bloques cae en el primer bloque de ese mismo período, lo cual es correcto como índice y equivocado como duración: el espacio entre dos bloques separados por 2.015 contiene 2.015 intervalos, no 2.016. Esa cifra se divide después por 1.209.600 segundos, lo que se suponía que iban a tardar 2.016 intervalos.

Hagamos el punto fijo. Con hashrate estable y marcas de tiempo honestas, el reajuste deja de moverse cuando nActualTimespan es igual a 1.209.600, y ese lapso cubre 2.015 intervalos. Entonces el intervalo al que converge el código es 1.209.600 / 2.015, o sea 600,2978 segundos: diez minutos y unas tres décimas de segundo. El sesgo corre hacia lo lento, no hacia lo rápido, en torno al 0,05 por ciento, y cada período de dos semanas se pasa unos diez minutos, que es exactamente el intervalo que quedó afuera. Ninguna fuente publica esta cifra, así que conviene revisar la aritmética en lugar de creerla.

Ese es un estado estacionario idealizado, no una medición. El intervalo promedio desde el bloque génesis hasta la altura 963.283 el 20 de agosto de 2026 da 577 segundos, nueve minutos y 37 segundos. Eso no dice nada sobre el error de uno. Dice que el hashrate creció durante diecisiete años, así que los bloques dentro de cada período siguen llegando más rápido que el objetivo fijado al comienzo, y un sesgo del 0,05 por ciento en la dirección contraria desaparece debajo.

La deriva es la mitad inofensiva. Lo que importa es que las ventanas de medición no se solapan. Una ventana va del primer bloque de un período al último, y la siguiente arranca en el bloque posterior a ese, así que el intervalo entre dos períodos no lo mide nadie. Una marca de tiempo equivocada o deshonesta en el bloque que cierra una ventana nunca queda descontada por la siguiente. Ese hueco es la puerta por la que entra el ataque time warp.

Corregir el error de uno exigiría un hard fork, y la razón es una línea de validation.cpp:

if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
    return state.Invalid(..., "bad-diffbits", "incorrect proof of work");

El nBits de una cabecera tiene que ser exactamente igual a lo que calcula el nodo que valida. No hay "más o menos", ni una dirección que sea más permisiva, así que cambiar la fórmula en cualquier sentido implica que los nodos actualizados y los no actualizados rechazan los bloques de los otros. Una división de cadena, por un 0,05 por ciento. Nadie propuso pagar eso.

Las marcas de tiempo no son un reloj

Todo el ajuste corre sobre números que los mineros escriben en sus propias cabeceras. Dos reglas de consenso en ContextualCheckBlockHeader los limitan, y ninguna es tan estricta como suele suponerse.

Una marca de tiempo tiene que ser estrictamente mayor que el median time past, la mediana de las once marcas de tiempo anteriores (chain.h). Como es una mediana de once y no una comparación contra el bloque padre, la marca de tiempo de un bloque puede ser legítimamente anterior a la de su padre. Las marcas de tiempo de los bloques no son monótonas, y el código que supone que lo son está equivocado.

Una marca de tiempo tampoco puede estar más de MAX_FUTURE_BLOCK_TIME, definido como 2 * 60 * 60 (chain.h), por delante del reloj propio del nodo que valida, no de uno de consenso: Bitcoin Core 27.0 quitó el tiempo ajustado por la red del código de consenso, así que quien opere un nodo cuyo reloj se desvíe lo suficiente se sale del consenso por su cuenta. Con espaciado normal, el median time past queda unos cinco bloques atrás, más o menos cincuenta minutos por detrás de la punta, así que la franja legal para una marca de tiempo nueva ronda las tres horas de ancho.

El reajuste lee las marcas de tiempo crudas de las cabeceras, no el median time past. Así que el minero del último bloque de un período, y el del primero, tienen cada uno un par de horas de margen sobre una magnitud que nominalmente vale 1.209.600 segundos. Dos horas sobre 336 es alrededor del 0,6 por ciento en cada extremo: real, acotado y de poco valor una sola vez. Hacerlo período tras período es el ataque time warp, que necesita hashrate mayoritario sostenido y a la vista de todos. La contramedida existe: una regla que limita cuánto puede quedar la marca de tiempo de un bloque de reajuste por detrás de la de su predecesor, en 600 segundos en testnet4 bajo BIP 94 y en 7.200 segundos en la propuesta para mainnet, BIP 54. Ninguna aplica acá. BIP 54 no tiene parámetros de activación, y chainparams.cpp deja enforce_BIP94 = false para mainnet. Y BIP 54 tampoco cerraría el error de uno: su segunda regla está escrita ella misma en términos del bloque 2.015 hacia atrás.

Cuando el hashrate se va, la red espera

Cuando China ordenó a sus mineros que pararan en 2021 y cerca de la mitad del hashrate de la red se apagó, los bloques se hicieron más lentos y siguieron lentos durante semanas. El 3 de julio de 2021 la dificultad cayó 27,94 por ciento, el mayor movimiento a la baja registrado, y el intervalo volvió.

No había un camino más rápido. El objetivo es constante durante 2.016 bloques por construcción, la única entrada son las marcas de tiempo transcurridas, y el único momento en que se leen es el límite del período. Reaccionar antes implicaría muestrear ventanas más cortas, y las ventanas cortas son más baratas de mover con exactamente el margen descrito arriba. Bitcoin compra robustez negándose a ser reactivo, y el precio lo pagan en tiempos de confirmación quienes están transaccionando durante el hueco. Es incómodo, y es el sistema funcionando.

Lo que no hace

  • No apunta a un precio. Ningún precio entra en el cálculo. La causalidad va al revés: el precio mueve el hashrate, el hashrate mueve el próximo reajuste.
  • No responde a la demanda de espacio en bloque. Un mempool lleno cambia las comisiones, no la dificultad.
  • No se ajusta de forma continua. Un escalón cada 2.016 bloques, y después plano.
  • No lleva un calendario. Los halvings se disparan por altura de bloque, así que una cadena lenta llega a ellos más tarde en tiempo de reloj y acuña exactamente la misma cantidad de monedas.
  • No garantiza diez minutos. Apunta a la media de un proceso sin memoria. Incluso en equilibrio perfecto los intervalos se distribuyen de forma exponencial: alrededor del 63 por ciento de los bloques llegan dentro de los diez minutos del anterior, y cerca de uno de cada 400 tarda más de una hora. Un bloque lento aislado no es evidencia de nada.

Todo el aparato son dos marcas de tiempo, una división y un tope, evaluados de forma idéntica por cada nodo. Todo lo que Bitcoin hace ante un shock de hashrate, lo hace en ese límite, y en ningún otro lado.

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