Kryptomonedas
Bitcoin

Los errores de Segwit2x – Explicados

No_Bitcoin_Segwit2x

La bifurcación de bitcoin Segwit2x se suspendió hace poco más de una semana mediante una publicación por correo electrónico a la lista de correo 2x. Varias partes amenazaron con dividir la red de todos modos, y muchos esperaban el bloque 494784 para ver si alguien minaría la bifurcación 2x o no.

Resultó que había un error en el software Segwit2x que hizo que el cliente se detuviera en el bloque 494782. En este artículo examinaremos los detalles de qué causó que el software se detuviera, por qué se detuvo un bloque antes de lo esperado y qué habría sucedido si Belshe y los demás no hubieran cancelado la bifurcación una semana antes.

La puesta en marcha

La parte 2x de la bifurcación dura se había planeado durante los seis meses anteriores. El acuerdo de Nueva York se acordó a fines de mayo, el código se escribió principalmente en junio y el software btc1 / Segwit2x se lanzó en julio.

Los detalles del NYA requerían que:

  1. Segwit se activara al 80% en vez del 95%
  2. La bifurcación dura 2x se activara en 6 meses (a partir del 23 de mayo)

El software se creó en el repositorio btc1, con Jeff Garzik como desarrollador principal. Para que se cumpliera la primera condición, incorporaron la propuesta BIP91 de James Hilliard, que activó Segwit en la red el 24 de agosto, en el bloque 481824.

Para que se cumpliera la segunda condición, el software btc1 incluía una cláusula que activaba la bifurcación dura para duplicar el tamaño del bloque exactamente 144 * 90 bloques después de la activación de Segwit. Se eligió este número porque 10 minutos por bloque significan alrededor de 144 bloques por día, de modo que 144 * 90 bloques tardarían unos 90 días. Esto situaba la altura del bloque de bifurcación en 494784 y la bifurcación real alrededor del 15 de noviembre, lo que satisfaría la segunda parte del NYA.

El error

Hubo un número limitado de diferencias en la base de código btc1 en comparación con Bitcoin Core. En total hubo alrededor de 500 líneas de cambios, la mayoría de las cuales no estaban consensuadas. Sin embargo, hubo al menos dos errores en las aproximadamente 100 líneas modificadas para soportar una bifurcación dura en el bloque 494784.

Para entender este error, conviene echar un vistazo al conjunto de cambios que introdujo por primera vez la idea de la bifurcación tras 144 * 90 bloques:

No_Bitcoin_Segwit2x_2 No_Bitcoin_Segwit2x_3

Puede verse que hay un parámetro de cuánto tardaría la activación de Segwit en duplicar el tamaño del bloque: en concreto, 144 * 90 bloques. En el código, esto se llama “Segwit seasoning”. Básicamente, permite que Segwit exista por sí mismo sin duplicar el tamaño del bloque durante 144 * 90 bloques.

Para determinar si es el momento de permitir bloques más grandes, la variable booleana fSegwitSeasoned se establece en True si han pasado 144 * 90 bloques, y en False si no es así. La siguiente instrucción if utiliza ese valor booleano para determinar cuál es el tamaño máximo del bloque base, es decir, el tamaño de bloque menos los datos de testigos (2 MB si es True, 1 MB si es False). Normalmente los bloques base se rechazarían si el bloque supera 1 MB, pero aquí vemos que los bloques se rechazan si superan 2 MB cuando fSegwitSeasoned es True. Esta es la parte crítica del código de consenso que rechaza los bloques demasiado grandes y que, por lo tanto, exige una bifurcación dura para separarse de la cadena original.

Para determinar realmente si fSegwitSeasoned debe establecerse en True o False, el código utiliza la función VersionBitsState. En concreto, se supone que el código mira el bloque situado 144 * 90 bloques atrás y comprueba si Segwit estaba activo en la red. Si Segwit estuvo activo hace 144 * 90 bloques, eso significa que los bloques base de más de 1 MB son legales para este bloque. Eso es lo que se supone que debe comprobar.

VersionBitsState

Aquí hay un error sutil que tiene que ver con cómo se llama a VersionBitsState. Para comprenderlo, echemos un vistazo a la función real definida en versionbits.cpp:

No_Bitcoin_Segwit2x_4

Esto parecerá un galimatías a menos que se conozca la base de código, pero lo explico. El primer argumento de la función VersionBitsState debe ser un puntero a un bloque. El nombre de variable pindexPrev indica que no es el puntero del bloque en sí, sino el del padre del bloque. De hecho, cualquier otra llamada a VersionBitsState en el archivo validation.cpp utiliza el puntero al bloque padre, no al bloque en sí, precisamente por ese motivo.

Aquí está el problema: pindexForkBuffer, arriba, está 144 * 90 bloques antes del bloque actual, no es el padre del bloque actual. En esencia, estamos comprobando si el bloque situado 144 * 90, 1 antes del actual tiene Segwit activado o no. Estamos desfasados en un bloque y, por lo tanto, los bloques más grandes se activan un bloque antes.

¿Cómo no vieron el error?

Este conjunto concreto de cambios formaba parte de una solicitud de extracción mucho más grande. Esa solicitud tiene 221 comentarios, la mayoría de los cuales discuten la definición de bloques de 2 MB. Puede verse que este commit concreto no aparece en la solicitud de extracción hasta mucho más abajo en la página. Solo una persona parece haber aprobado los cambios (opetruzel) y, cerca del final, hay quejas de deadalnix (conocido por Bitcoin Cash) sobre que esta solicitud de extracción no tenía pruebas suficientes.

Errores sobre errores

Este fragmento de código del cálculo de los 144 * 90 bloques pasados se aceptó como la forma correcta de hacer las cosas y nadie advirtió que esto causaría problemas más adelante.

Para asegurarse de que la bifurcación 2x no fuera rebasada por la cadena 1x y reorganizada (básicamente, aniquilada por completo), se estableció una regla de protección contra wipeout. Esta exige que el bloque de la bifurcación tenga un tamaño de bloque base superior a 1 MB. Se usó la misma lógica que antes y, en esencia, es el bloque forzado 494783 el que tiene un tamaño de bloque base de más de 1 MB, no el bloque 494784.

Por eso btc1 quedó trabado en 494782: el software btc1 está esperando un bloque base de más de 1 MB en 494783.

Pero hay más

Como si este error de orden no fuera suficiente, hay otro error en el código de BlockAssembler. BlockAssembler forma parte de miner.cpp, el código responsable de crear nuevos bloques. En general, esto es código útil solo para los mineros, ya que son los únicos que realmente crean bloques nuevos.

En concreto, la variable fWitnessSeasoned no se inicializa, pero se usa. Esto es un comportamiento indefinido, como ha demostrado Pieter Wuille.

No_Bitcoin_Segwit2x_5

No_Bitcoin_Segwit2x_6

¿Por qué importa esto? Resulta que esta variable determina el tamaño máximo de bloque y el peso que generará el software. Si la variable es falsa, el software nunca formará un bloque lo bastante grande como para bifurcar la cadena, ya que el peso máximo del bloque será de 4.000.000 y no de 8.000.000, como exigen las especificaciones de la bifurcación 2x. Por el contrario, si la variable es verdadera, el software generará bloques inválidos antes de la bifurcación de la cadena. De modo que era posible que, incluso si un minero quería minar la 2x, este software no se lo permitiera.

Este cambio de código se introdujo en este pull request y, de nuevo, Jeff Garzik fue el autor y fusionó el código quizá con un solo revisor (FaysalM), que no detectó el error.

¿Qué habría pasado si la 2x no se hubiera cancelado?

Los mineros que planeaban probar la 2x habrían pensado, como es natural, que 494784 era el bloque, ya que Jeff Garzik y el equipo segwit2x lo habían declarado en numerosas ocasiones.

Incluso si los mineros no usaban el código anterior que posiblemente les habría impedido crear bloques más grandes, habrían personalizado su software para encontrar bloques más grandes en el bloque 494784, no en el 494783. Esto habría causado el mismo bloqueo en el bloque 494782 y todos habrían empezado a tratar de depurar qué estaba causando el problema.

Lo más probable es que algún minero hubiese resuelto las cosas y simplemente hubiera minado un bloque grande para la bifurcación 2x de todos modos. No se sabe con certeza cuánto tiempo habría llevado, pero está bastante claro que habría sido un desastre de relaciones públicas.

Más aún, como señala Greg Maxwell, los intercambios habrían congelado cuentas a partir del bloque 494784, no del 494783, por lo que todos los saldos de las monedas 2x habrían quedado fuera de sincronía respecto a quién entró en el bloque 494783. Esto, de nuevo, habría causado daños graves.

Conclusión

Revisar y probar los cambios de consenso es muy difícil. Parece que btc1 tuvo exactamente un programador y un revisor para estos cambios críticos de consenso, y eso simplemente no basta para detectar errores sutiles como el primero ni errores evidentes como el segundo. Además, como el cambio de uno a uno se aceptó en una fecha bastante temprana (alrededor del 15 de junio), después, cuando el código se usó para la protección contra wipeout, se asumió que era correcto por una “revisión” previa.

En esencia, incluso una o dos revisiones débiles en una cadena de revisiones pueden romper todo el sistema de consenso con un error catastrófico.

Por suerte, esto puede servir de lección para garantizar que los cambios críticos se revisen de manera exhaustiva. Manténgase seguro y dé las gracias a los desarrolladores que hacen el trabajo duro no solo de codificación, sino de revisión.

Aquí les dejamos el pull request que causó todo esto.

↑ Volver arriba