Probando bifurcaciones de blockchain

Quiero probar si un servicio puede administrar una bifurcación de blockchain. Sin embargo, no hay forma de obtener bitcoind para crear bifurcaciones de prueba en el modo de prueba. Intenté modificarlo para generar bloques con hashes prevblock arbitrarios, pero no es fácil debido a la alta complejidad del código. Logré que creara bifurcaciones además de bloques anteriores, sin embargo, no puedo lograr que genere bloques para transacciones en conflicto (gastos dobles y cambios de maleabilidad), en gran parte debido a todo el código de "poda" . Seguramente alguien ya ha encontrado una solución, ya que esto es algo vital para probar. No puedo encontrar nada en línea que me ayude a probar escenarios de bifurcación.

Estoy pensando que tal vez debería haber usado otra técnica. Que debería haber ejecutado 3 nodos de registro de bitcoind y aislarlos para poder crear múltiples cadenas de bloques. 2 de ellos generan cadenas competidoras, y el otro interactúa con la aplicación. ¿Puedo generar un escenario de bifurcación con los dos primeros nodos, sincronizar uno de los nodos con el tercer nodo y luego sincronizar el otro nodo para generar la bifurcación? Necesitaría usar una combinación de addnode y tcpkill (?) para sincronizar y luego volver a aislar los nodos.

Esto parece ridículo para probar algo tan simple, entonces, ¿hay una mejor manera?

Respuestas (3)

Puede usar los comandos RPC invalidateblock para crear bifurcaciones de blockchain.

invalidateblock hash le dice a un nodo que considere que el hash no es válido, así que simplemente genere un montón de bloques, invalide uno en algún lugar de la cadena en uno de los nodos de registro y haga que ese nodo genere un montón más para crear una bifurcación.

Consulte qa/rpc-tests/mempool_resurrect_test.py en el árbol de Bitcoin Core para ver un ejemplo de Python.

Gracias. No conocía estos comandos RPC ya que no aparecen en el comando de ayuda. Sin embargo, un problema es que cuando invalida un bloque, las transacciones alquilarían el mempool. Esto no permitiría probar escenarios de transacciones en conflicto confirmadas en bifurcaciones, a menos que también hubiera un comando que eliminaría/reemplazaría transacciones del mempool.
@gavinandresen: ¿hay algún RPC que no sea reconsiderblockel que considera válida una bifurcación sin cerrar la bifurcación? Tengo un escenario en el que me gustaría tener 2 bifurcaciones válidas para 2 nodos conectados (!).
¿Algún otro comando que esté oculto en la ayuda? Estos parecen bastante útiles para fines de desarrollo.
Vale la pena señalar que si usa este método, el comportamiento de ZMQ no será como se describe en los documentos para reorganizaciones adecuadas ( github.com/bitcoin/bitcoin/blob/master/doc/zmq.md ). Digamos que tiene la cadena A -> B -> C, luego invalide C, luego genere dos nuevos bloques, produciendo A -> B -> C' -> D. ZMQ notificará a A, B, C, C' y D, mientras que los documentos implican que, en las reorganizaciones adecuadas, ZMQ solo notificaría a A, B, C y D.

Logré probar bifurcaciones creando 3 nodos de prueba de registro. Un nodo se usa para interactuar con mi software, y otros dos nodos se usan para contener cadenas de bloques competidoras. El modo regtest inicia cada nodo sin conexiones para empezar, por lo que están completamente aislados. Puedo usar "sendrawtransaction" y "setgenerate" en estos nodos aislados para crear cadenas competidoras. Sin embargo, los nodos deben sincronizarse para probar escenarios de bifurcación.

Resultó ser fácil sincronizar nodos. Sincronizo los nodos de la competencia con el nodo de la interfaz con la ayuda del comando RPC "disconnectpeer" que puede encontrar aquí . Mi código de sincronización es el siguiente en pseudocódigo:

function sync (node1, node2)

    if node2.height < node1.height then
        sync(node2, node1)
        return

    node1.rpc("addnode", "localhost:" + node2.port, "onetry")

    wait while node1.rpc("get_info")["blocks"] is not node1.height

    node1.height = node2.height
    node1.rpc("disconnectpeer", node1.rpc("getpeerinfo")[0]["id"])

Una vez completada la función, los nodos permanecerán aislados una vez más para que pueda trabajar en la creación de nuevas cadenas competidoras.

También puede usar los comandos RPC invalidateblocky reconsiderblockpara ayudar con las pruebas en este escenario. Le permiten desconectarse y reorganizarse hacia adelante y hacia atrás en situaciones estáticas.
@MatthewMitchell: ¿Cuál es la idea aproximada de usar un "nodo de interfaz"? ¿No es suficiente tener 2 nodos solamente?
@AliakbarAhmadi: La idea es crear dos cadenas de bloques competidoras con dos nodos y el "nodo de interfaz" se sincroniza con cualquiera de ellos. Una vez que este nodo de interfaz está sincronizado, no puede crear su propia bifurcación (a menos que lo hayas usado invalidateblockcomo sugirió Gavin), por eso tienes el tercer nodo para mantenerlo aislado mientras los otros dos están sincronizados. Luego, usa este tercer nodo para crear otra bifurcación si lo desea para su caso de prueba.
Hoy en día parece que disconnectpeerse reemplaza por (i) addnodecon comando removeo (ii) disconnectnode.

Dependiendo de su idioma y biblioteca de bitcoin, puede simular un par de bitcoin falso. Esto es lo que hago cuando desarrollo con NBitcoin. (C#)

Básicamente, configuro 2 pares, el "cliente" y el "servidor", luego configuro el cliente para que "confíe" en mi nodo de servidor en las pruebas. (lo que significa que no verifica la corrección del bloque y la prueba de trabajo)

Luego pruebo el comportamiento del servidor, ya que quiero probar cualquier escenario de bifurcación.

Pensé en crear un nodo simulado, aunque en cambio logré crear una bifurcación aislando los nodos de prueba, que describiré en una respuesta separada. Gracias.