La bifurcación de bitcoin Segwit2x fue suspendido hace poco más de una semana en una publicación por correo electrónico a la lista de correo 2x. Varias partes amenazaron con dividir la red de todos modos, y esperamos con impaciencia el bloque 494784 para ver si alguien extraerí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 los esperado y lo que habría sucedido si Belshe, et al, no hubieran cancelado la bifurcación una semana antes.
La puesta en marcha
La parte 2x de la bifurcación dura había sido planeada durante los últimos 6 meses. El acuerdo de Nueva York fue acordado a fines de mayo, el código fue escrito principalmente en junio y el software btc1 / Segwit2x fue lanzado en julio.
Los detalles del NYA requieren que:
- Segwit se activará al 80% en vez del 95%
- 2x hard fork se activará en 6 meses (a partir del 23 de mayo)
El software fue creado en el repositorio btc1, con el desarrollador principal, Jeff Garzik. Para hacer que la primera condición suceda, incorporaron la propuesta BIP91 de James Hilliard, que efectivamente activó a Segwit en la red el 24 de agosto en el bloque 481824.
Para hacer que la segunda condición suceda, el software btc1 incluía una cláusula que activaba 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 significa alrededor de 144 bloques por día, por lo que 144 * 90 bloques tardarían unos 90 días. Esto puso la altura del bloque de bifurcación en 494784 y la bifurcación real alrededor del 15 de noviembre más o menos, lo que de hecho 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 fueron 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, podría ser más fácil echar un vistazo al conjunto de cambios que introdujeron por primera vez la idea de bifurcación después de 144 * 90 bloques más:
Puede ver que hay un parámetro de cuánto tardaría la activación de Segwit en duplicar el tamaño del bloque. Específicamente 144 * 90 bloques. En el código, este es un concepto llamado “Segwit seasoning”. Básicamente, esto permite que Segwit exista por sí mismo sin duplicar el tamaño del bloque para 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, False si no es así. La siguiente instrucción if utiliza específicamente este valor booleano para determinar cuál es el tamaño máximo del bloque base (tamaño de bloque menos datos de testigos) (2mb si es verdadero, 1mb si es falso). Normalmente, los bloques base serían rechazados si el bloque es mayor que 1mb, pero aquí, vemos que los bloques son rechazados si el bloque es mayor a 2mb si fSegwitSeasoned es True. Esta es la parte crítica del código de consenso que rechaza bloques demasiado grandes y, por lo tanto, requiere una bifurcacion dura para separarse de la cadena original.
Para averiguar realmente si fSegwitSeasoned debe establecerse en True o False, el código aquí utiliza la función VersionBitsState. Específicamente, se supone que el código mira el bloque 144 * 90 bloques previos y comprueba si Segwit estaba activo en la red. Si Segwit estuvo activo hace 144 * 90 bloques, eso significa que los bloques base de> 1 MB son legales para este bloque. Eso es lo que se supone que debe probar.
VersionBitsState
Aquí hay un error sutil y tiene que ver con cómo se llama VersionBitsState. Para comprender, eche un vistazo a la función real definida en versionbits.cpp:
Esto se verá como un galimatías a menos que sepa algo sobre la base de código, pero permítame explicarlo. El primer argumento de la función VersionBitsState se supone que es un puntero a un bloque. El nombre de variable pindexPrev indica que no es el puntero del bloque en sí, sino el padre del bloque. De hecho, cualquier otra llamada a VersionBitsState en el archivo validation.cpp utiliza específicamente el puntero al bloque padre, no el bloque en sí mismo por ese motivo.
Aquí está el problema: pindexForkBuffer arriba es 144 * 90 bloques antes del bloque actual, no el padre del bloque actual. Entonces, en esencia, estamos viendo si el bloque 144 * 90-1 antes del actual tiene activado Segwit o no. Estamos fuera de 1 bloque y, por lo tanto, los bloques más grandes se activan 1 bloque antes.
¿Cómo no vieron el error?
Este conjunto particular de cambios fue parte de una solicitud de extracción mucho más grande. La solicitud de extracción tiene 221 comentarios, la mayoría de los cuales discuten sobre la definición de bloques de 2MB. Puede ver que esta confirmación concreta en realidad no entra en esta solicitud de extracción hasta mucho más abajo en la página. Solo una persona parece haber aprobado los cambios (opetruzel) y hay quejas cerca del final por deadalnix (el de la fama de Bitcoin Cash) sobre esta solicitud de extracción que no tiene suficientes pruebas.
Errores en errores
Este fragmento de código de cálculo de 144 * 90 bloques pasado fue aceptado como la forma correcta de hacer las cosas y nadie captó el hecho de que esto causaría problemas más adelante.
Para asegurarse de que la bifurcacion 2x no fuera rebasada por la cadena 1x y reorganizada (básicamente, completamente aniquilada), se estableció una regla para la protección contra wipeout. Esto requiere 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 anteriormente y esencialmente el bloque forzado 494783 tiene un tamaño de bloque de base> 1 MB, no el bloque 494784.
Esta es la razón por la cual btc1 está trabada en 494782, porque el software btc1 está esperando un bloque de base mayor a 1MB en 494783.
Pero espera hay mas
Como si este error fuera de orden no fuera suficiente, hay otro error en el código de BlockAssembler. BlockAssembler es parte de miner.cpp, que es el código responsable de crear nuevos bloques. En general, esto es solo un código útil para los mineros, ya que son los únicos que realmente crean nuevos bloques.
Específicamente, la variable fWitnessSeasoned no se inicializa, pero se usa. Este es un comportamiento indefinido como lo ha demostrado Pieter Wuille.
¿Porque es esto importante? Bueno, resulta que esta variable en particular determina el tamaño máximo de bloque y el peso que el software hará. Si esta variable es falsa, entonces el software nunca formará un bloque lo suficientemente 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 según lo exijan las especificaciones de la bifurcación 2x. Por el contrario, si esta variable es verdadera, entonces el software generará bloques inválidos antes de la bifurcación de la cadena. ¡Así que era posible que incluso si un minero quisiera extraer el 2x, este software no los permitiera!
Este cambio de código se introdujo en este pedido de extracción y, una vez más, Jeff Garzik fue el autor y se unió al código con tal vez un revisor (FaysalM) que no detectó el error.
¿Qué hubiera pasado si 2x no hubiera sido cancelado?
Los mineros que planeaban jugar con 2x habrían pensado naturalmente que 494784 era el bloque, ya que Jeff Garzik y el equipo segwit2x han declarado en numerosas ocasiones que este era el bloque de bifurcación.
Incluso si los mineros no estaban usando el código anterior que posiblemente les hubiera impedido crear bloques más grandes, habrían personalizado su software para encontrar bloques más grandes para el bloque 494784, no 494783. Esto habría causado el mismo bloqueo en el bloque 494782 y todos lo harían han comenzado a tratar de depurar lo que estaba causando el problema.
Lo más probable es que algún minero hubiese averiguado las cosas y simplemente hubiera minado un gran bloque para la bifurcación 2x de todos modos. No se sabe con certeza cuánto tiempo hubiera costado, pero está bastante claro que esto hubiera sido un desastre de relaciones públicas.
Más que eso, como Greg Maxwell señala , los intercambios habrían congelado cuentas a partir del bloque 494784, no 494783, por lo que todos los saldos de las monedas 2x habrían estado fuera de sincronía con quién ingresó en el bloque 494783. Esto nuevamente habría causado daños graves.
Conclusión
Revisar y probar los cambios de consenso es realmente, muy difícil. Parece que btc1 tenía exactamente 1 codificador y 1 revisor para estos cambios críticos de consenso y eso simplemente no es suficiente para detectar errores sutiles como el primero o errores obvios como el segundo. Además, debido a que el cambio de uno a uno fue aceptado en una fecha bastante temprana (~ 15 de junio), más tarde cuando el código se usó para la protección contra wipeout, se asumió que el código era bueno debido a una “revisión” previa.
Esencialmente, incluso una o dos revisiones débiles en una cadena de revisiones pueden romper todo el sistema de consenso con un error catastrófico.
Afortunadamente, esta puede ser una lección objetiva para garantizar que los cambios críticos se revisen de manera exhaustiva. Manténgase seguro y dele las gracias a los desarrolladores que hacen el trabajo duro no solo de codificación, sino de revisión
Aqui te dejamos el pull request que causo todo esto