El Proceso IBD

Sincronizar un nuevo nodo con el tip de la red implica varias etapas distintas:

  • Descubrimiento de pares y selección de cadena, donde el nodo se conecta a pares al azar y determina la cadena con más trabajo acumulado.
  • Descarga de encabezados, cuando se obtienen los encabezados de bloques y se conectan para formar la cadena completa de encabezados.
  • Descarga de bloques, donde el nodo solicita bloques que pertenecen a esa cadena desde múltiples pares simultáneamente.
  • Validación de bloques y transacciones, donde se verifica cada transacción de bloque antes de procesar el siguiente.

Aunque la validación de bloques es inherentemente secuencial, cada bloque depende del estado generado por el anterior, gran parte del trabajo circundante se realiza en paralelo. La sincronización de encabezados, las descargas de bloques y la verificación de scripts pueden ocurrir simultáneamente en diferentes hilos. Un IBD ideal satura todos los subsistemas: hilos de red obteniendo datos, hilos de validación verificando firmas y hilos de base de datos escribiendo el estado resultante.

Sin mejoras continuas en el rendimiento, los nodos económicos pueden no ser capaces de unirse a la red en el futuro.

Introducción

La cultura de «no confíes, verifica» de Bitcoin exige que el libro mayor pueda ser reconstruido por cualquiera desde cero. Después de procesar todas las transacciones históricas, cada usuario debe llegar al mismo estado local de los fondos que el resto de la red.

Esta reproducibilidad está en el corazón del diseño de confianza minimizada de Bitcoin, pero tiene un costo significativo: tras casi 17 años, esta base de datos en constante crecimiento obliga a los nuevos usuarios a trabajar más que nunca antes de poder unirse a la red Bitcoin.

Al iniciar un nuevo nodo, debe descargar, verificar y persistir cada bloque desde el génesis hasta el tip actual de la cadena, un proceso de sincronización intensivo en recursos conocido como Descarga Inicial de Bloques (IBD).

Aunque el hardware de consumo sigue mejorando, mantener bajos los requisitos de IBD es crucial para preservar la descentralización, ya que asegura que la validación siga siendo accesible para todos, desde dispositivos de menor potencia como Raspberry Pis hasta servidores de alto rendimiento.

Proceso de evaluación

La optimización del rendimiento comienza con la comprensión de cómo interactúan los componentes de software, los patrones de datos, el hardware y las condiciones de la red para crear cuellos de botella en el rendimiento. Esto requiere una experimentación extensa, en su mayoría desechada. Más allá del habitual acto de equilibrio entre velocidad, uso de memoria y mantenibilidad, los desarrolladores de Bitcoin Core deben elegir los cambios de menor riesgo y mayor retorno. Las optimizaciones válidas pero menores a menudo se rechazan por ser consideradas demasiado arriesgadas en relación con su beneficio.

Disponemos de un conjunto significativo de micro-benchmarks para garantizar que la funcionalidad existente no degrade su rendimiento. Estos son útiles para detectar regresiones, es decir, retrocesos en el rendimiento en piezas individuales de código, aunque no son necesariamente representativos del rendimiento general de IBD.

Los contribuyentes que proponen optimizaciones proporcionan reproductores y mediciones a través de diferentes entornos: sistemas operativos, compiladores, tipos de almacenamiento (SSD vs HDD), velocidades de red, tamaños de caché de base de datos, configuraciones de nodos (recortados vs archivados) y combinaciones de índices. Escribimos benchmarks de uso único y utilizamos exploradores de compilador para validar qué configuración funcionaría mejor en ese escenario específico (por ejemplo, la verificación de transacciones duplicadas dentro del bloque utilizando Hash Set frente a Sorted Set o Sorted Vector).

También estamos evaluando regularmente el proceso de IBD. Esto se puede hacer reindexando el estado de la cadena y opcionalmente el índice de bloques a partir de archivos de bloques locales, o realizando un IBD completo ya sea desde pares locales (para evitar que los pares lentos afecten los tiempos) o desde la red p2p más amplia.

Los benchmarks de IBD a menudo muestran mejoras menores que los micro-benchmarks, ya que el ancho de banda de la red u otra entrada/salida (I/O) suele ser el cuello de botella; descargar la blockchain por sí sola lleva aproximadamente 16 horas con las velocidades promedio de internet global.

Para obtener una máxima reproducibilidad, se prefiere a menudo reindexar el estado de la cadena, creando perfiles de memoria y CPU antes y después de la optimización y validando cómo el cambio afecta la funcionalidad que en sí.

Mejoras históricas y en curso

Las primeras versiones de Bitcoin Core fueron diseñadas para una blockchain mucho más pequeña. El prototipo original de Satoshi sentó las bases, pero sin innovación constante de los desarrolladores de Bitcoin Core, no habría podido manejar el crecimiento sin precedentes de la red.

Originalmente, el índice de bloques almacenaba cada transacción histórica y si estaban gastadas o no, pero en 2012, “Ultraprune” (PR #1677) creó una base de datos dedicada para rastrear las salidas de transacciones no gastadas, formando el conjunto UTXO, que pre-cachea el último estado de todas las monedas gastables, proporcionando una vista unificada para la validación. Combinado con una migración de base de datos de Berkeley DB a LevelDB, la velocidad de validación mejoró significativamente.

Sin embargo, esta migración de base de datos causó la bifurcación de la cadena BIP50, cuando se aceptó un bloque con muchos inputs de transacción por nodos actualizados pero se rechazó por versiones más antiguas debido a su complejidad. Esto resalta cómo el desarrollo de Bitcoin Core difiere del típico desarrollo de software: incluso las optimizaciones de rendimiento puras pueden resultar en divisiones no deseadas de la cadena.

Al año siguiente (PR #2060) se habilitó la validación de firmas multihilo. Aproximadamente al mismo tiempo, se creó la biblioteca criptográfica especializada libsecp256k1, que se integró en Bitcoin Core en 2014. A lo largo de la siguiente década, a través de continuas optimizaciones, se volvió más de 8 veces más rápida que la misma funcionalidad en la biblioteca OpenSSL de propósito general.

La sincronización de encabezados primero (PR #4468, 2014) reestructuró el proceso de IBD para descargar primero la cadena de encabezados de bloques con el mayor trabajo acumulado, y luego obtener bloques de múltiples pares simultáneamente. Además de acelerar IBD, también eliminó el uso ineficiente de ancho de banda en bloques que serían huérfanos porque no estaban en la cadena principal.

En 2016, el PR #9049 eliminó lo que parecía ser una verificación redundante de inputs duplicados, introduciendo un error de consenso que podría haber permitido la inflación de suministro. Afortunadamente, fue descubierto y corregido antes de la explotación. Este incidente impulsó grandes inversiones en recursos de pruebas. Hoy en día, con fuzzing diferencial, amplia cobertura y una disciplina de revisión más estricta, Bitcoin Core identifica y resuelve problemas mucho más rápido, sin informes de peligros de consenso comparables desde entonces.

En 2017, el PR #9484 separó las verificaciones de validez de bloque generales de la costosa verificación de firma, haciendo esta última opcional para la mayor parte de IBD, reduciendo su tiempo aproximadamente a la mitad. La estructura de bloques, la prueba de trabajo y las reglas de gasto permanecen completamente verificadas: -assumevalid omite las verificaciones de firmas para todos los bloques hasta una cierta altura de bloque.

En 2022, el PR #25325 reemplazó el asignador de memoria ordinario de Bitcoin Core con un asignador personalizado basado en grupos optimizado para la caché de monedas. Al diseñarse específicamente para los patrones de asignación de Bitcoin, redujo el desperdicio de memoria y mejoró la eficiencia de la caché, proporcionando un IBD aproximadamente un 21% más rápido mientras encajaba más monedas en la misma huella de memoria.

Aunque el código en sí no se deteriora, el sistema en el que opera evoluciona constantemente. Cada 10 minutos, el estado de Bitcoin cambia: los patrones de uso cambian y los cuellos de botella migran. El mantenimiento y la optimización no son opcionales; sin adaptación constante, Bitcoin acumularía vulnerabilidades más rápido de lo que podría defenderse una base de código estática, y el rendimiento de IBD retrocedería a pesar de los avances en hardware.

El creciente tamaño del conjunto UTXO y el aumento del peso promedio del bloque ejemplifican esta evolución. Las tareas que antes estaban limitadas por la CPU (como la verificación de firmas) ahora a menudo están limitadas por Entrada/Salida (I/O) debido al acceso más pesado al estado de la cadena (teniendo que verificar el conjunto UTXO en el disco). Este cambio ha llevado a nuevas prioridades: mejorar la caché de memoria, reducir la frecuencia de descarga de LevelDB y paralelizar las lecturas desde disco para mantener ocupadas a las modernas CPUs de múltiples núcleos.

Optimización reciente

Los diseños de software se basan en patrones de uso predeterminados, que inevitablemente se desvían de la realidad a medida que la red evoluciona. La carga de trabajo determinista de Bitcoin nos permite medir el comportamiento real y corregir el rumbo luego, asegurando que el rendimiento mantenga el ritmo del crecimiento de la red.

Estamos ajustando constantemente las configuraciones predeterminadas para que se adapten mejor a los patrones de uso del mundo real. Algunos ejemplos:

  • El PR #30039 aumentó el tamaño máximo de archivo de LevelDB, un simple cambio de parámetro que proporcionó aproximadamente un 30% de aumento en la velocidad de IBD al alinearse mejor con cómo se accede realmente a la base de datos del estado de la cadena (conjunto UTXO).
  • El PR #31645 duplicó el tamaño del lote de flush, reduciendo escrituras en disco fragmentadas durante la fase más intensiva en escritura de IBD y acelerando el ahorro de progreso cuando IBD se interrumpe.
  • El PR #32279 ajustó el tamaño de almacenamiento interno del prevector (utilizado principalmente para el almacenamiento en memoria de scripts). El antiguo umbral previo a segwit priorizaba plantillas de script más antiguas en detrimento de las más nuevas. Al ajustar la capacidad para cubrir los tamaños de script modernos, se evitan asignaciones de memoria, se reduce la fragmentación de memoria y la ejecución de scripts se beneficia de una mejor localidad de caché.

Todos cambios pequeños, quirúrgicos, con impactos de validación medibles.

Más allá de la optimización de parámetros, algunos cambios requerían repensar los diseños existentes:

  • El PR #28280 mejoró la forma en que los nodos recortados (que descartan bloques antiguos para ahorrar espacio en disco) manejan los flushes frecuentes de la caché de memoria. El diseño original vertía toda la caché o la escaneaba para encontrar entradas modificadas. Realizar un seguimiento selectivo de las entradas modificadas permitió una aceleración de más del 30% para los nodos recortados con el máximo dbcache y ~9% de mejora con configuraciones predeterminadas.
  • El PR #31551 introdujo procesamiento por lotes de lectura/escritura para archivos de bloques, reduciendo la sobrecarga de muchas operaciones de sistema de archivos pequeñas. La mejora de 4x-8x en el acceso a archivos de bloques mejoró no solo IBD, sino también otros RPC.
  • El PR #31144 optimizó la obfuscación de archivos de bloques existente (utilizada para asegurar que los datos no se almacenen en texto claro en disco) procesando bloques de 64 bits en lugar de operaciones byte a byte, ofreciendo otra aceleración en IBD. Con la obfuscación siendo esencialmente gratuita, los usuarios ya no necesitan elegir entre almacenamiento seguro y rendimiento.

Otras optimizaciones menores de caché (como el PR #32487) permitieron añadir verificaciones adicionales de seguridad que antes se consideraban demasiado costosas (PR #32638).

De manera similar, ahora podemos vaciar la caché con mayor frecuencia en disco (PR #30611), asegurando que los nodos nunca pierdan más de una hora de trabajo de validación en caso de fallos. La modesta sobrecarga fue aceptable porque las optimizaciones previas ya habían hecho que IBD fuera significativamente más rápido.

El PR #32043 sirve actualmente como un rastreador para mejoras de rendimiento relacionadas con IBD. Agrupa un docena de esfuerzos en curso, desde la optimización de disco y caché hasta mejoras en la concurrencia, y proporciona un marco para medir cómo cada cambio afecta al rendimiento en el mundo real. Este enfoque anima a los contribuyentes a presentar no solo código, sino también benchmarks reproducibles, datos de perfilado y comparaciones entre hardware.

Sugerencias para futuras optimizaciones

El PR #31132 paraleliza la obtención de inputs de transacciones durante la validación de bloques. Actualmente, cada input se obtiene del conjunto UTXO secuencialmente – los fallos de caché requieren viajes de disco, creando un cuello de botella de I/O. Este PR introduce la obtención en paralelo a través de múltiples hilos de trabajo, logrando hasta un ~30% más rápido -reindex-chainstate (~10 horas en una Raspberry Pi 5 con 450 MB de dbcache). Como efecto secundario, esto estrecha la brecha de rendimiento entre valores bajos y altos de -dbcache, permitiendo potencialmente que nodos con memoria modesta se sincronicen casi tan rápido como configuraciones de alta memoria.

Aparte de IBD, el PR #26966 paraleliza la construcción de índices de bloques y transacciones utilizando hilos de trabajo configurables.

Mantener compacto el conjunto UTXO persistido es crucial para la accesibilidad del nodo. El PR #33817 experimenta con reducirlo ligeramente al eliminar una función opcional de LevelDB que podría no ser necesaria para el caso de uso específico de Bitcoin.

SwiftSync es un enfoque experimental que aprovecha nuestra perspectiva sobre bloques históricos. Sabiendo el resultado real, podemos categorizar cada moneda encontrada por su estado final en la altura objetivo: aquellas que siguen sin gastarse (que almacenamos) y aquellas gastadas hasta esa altura (que podemos ignorar, verificando simplemente que aparecen en pares de creación/gasto coincidentes en cualquier parte). Las pistas pre-generadas codifican esta clasificación, permitiendo a los nodos omitir completamente las operaciones UTXO para monedas de corta duración.

Bitcoin Es Abierto Para Todos

Más allá de los benchmarks sintéticos, un experimento reciente corrió el prototipo de SwiftSync en una Raspberry Pi 5 subclockeada alimentada por una batería a través de WiFi, completando la -reindex-chainstate de 888,888 bloques en 3 h 14 m. Las mediciones con configuraciones equivalentes muestran una mejora del 250% en la velocidad de validación completa a través de versiones recientes de Bitcoin Core.

Años de trabajo acumulado se traducen en un impacto genuino: validar completamente casi un millón de bloques ahora puede hacerse en menos de un día en hardware económico, manteniendo la accesibilidad a pesar del crecimiento continuo de la blockchain.

La soberanía personal es más accesible que nunca.



¡Adquiere tu copia de The Core Issue hoy!

No te pierdas la oportunidad de tener The Core Issue, que incluye artículos escritos por muchos desarrolladores de Core que explican los proyectos en los que trabajan.

Este artículo es la Carta del Editor, presentada en la última edición impresa de Bitcoin Magazine, The Core Issue. Lo compartimos aquí como un adelanto de las ideas exploradas en todo el número.


[1] https://github.com/bitcoin/bips/blob/master/bip-0050.mediawiki

[2] https://en.bitcoin.it/wiki/Common_Vulnerabilities_and_Exposures

[3] https://delvingbitcoin.org/t/swiftsync-speeding-up-ibd-with-pre-generated-hints-poc/1562

[4] https://x.com/L0RINC/status/1972062557835088347

[5] https://x.com/L0RINC/status/1970918510248575358

Todas las Solicitudes de Extracción (PR) enumeradas en este artículo pueden buscarse por número aquí: https://github.com/bitcoin/bitcoin/pulls