Me gustaría inducir de manera confiable una bifurcación en una red de prueba privada para poder probar el comportamiento del código fuera de la cadena de bloques que interactúa con los contratos. (ver preguntas relacionadas sobre por qué querría hacer eso). Probablemente sería útil para pruebas automatizadas de geth y otras implementaciones de nodos también.
Por confiable, me refiero a repetible, con el mismo resultado cada vez, para que pueda ejecutar esto en un sistema de prueba automatizado.
La solución
admin.addPeer(...)
comando en geth para agregar nodos pares a la lista de pares de geth, pero no parece haber ningún comando para eliminar estos pares.Los detalles de la prueba son los siguientes:
La prueba
La primera instancia de geth usa el puerto P2P 30301 con el siguiente comando:
user@Kumquat:~/ForkIt$ geth --datadir ./data1 \
--genesis ~/ForkIt/etc/CustomGenesis.json \
--networkid 8888 --nodiscover --mine --minerthreads 1 \
--port 30301 --maxpeers 10 console
La segunda instancia de geth usa el puerto P2P 30302 con el siguiente comando:
user@Kumquat:~/ForkIt$ geth --datadir ./data2 \
--genesis ~/ForkIt/etc/CustomGenesis.json \
--networkid 8888 --nodiscover --mine --minerthreads 1 \
--port 30302 --maxpeers 10 console
La primera instancia de geth usa el archivo ./data1/static-nodes.json
para encontrar la segunda instancia de geth. Este archivo contiene la información de enodo obtenida usando el admin.nodeInfo
comando de la segunda instancia de geth, donde reemplazo el texto [::] con la dirección IP de mi computadora. Así es ./data1/static-nodes.json
como se ve mi:
[
"enode://3941d48d95d4782f8b4fb7561d78642d2e53e478e5c8d3087e6e6023f5931aca3024a9679628fff775b9ddafd11d8d48f84a502fe815619be80f16b54cb1c077@192.168.1.14:30302"
]
La segunda instancia de geth usa el archivo ./data2/static-nodes.json
con la información de enodo obtenida usando el admin.nodeInfo
comando de la primera instancia de geth
Para simular la bifurcación, bloqueo los puertos TCP 30301 y 30302 usando los siguientes comandos:
user@Kumquat:~/ForkIt$ sudo iptables -A INPUT -p tcp --dport 30301 -j DROP
user@Kumquat:~/ForkIt$ sudo iptables -A INPUT -p tcp --dport 30302 -j DROP
Para permitir que las instancias geth se vuelvan a conectar como pares, elimino las reglas de bloqueo en los puertos TCP 30301 y 30302 usando los siguientes comandos:
user@Kumquat:~/ForkIt$ sudo iptables -D INPUT -p tcp --dport 30301 -j DROP
user@Kumquat:~/ForkIt$ sudo iptables -D INPUT -p tcp --dport 30302 -j DROP
La conexión y desconexión P2P
Para ver el seguimiento de la conexión P2P entre las instancias de geth, ejecute el comando admin.verbosity(6)
en la consola de geth.
Cuando los puertos están desbloqueados
El nodo 1 muestra el siguiente mensaje:
I0412 10:11:47.379824 12467 peer.go:173] Peer 3941d48d95d4782f 192.168.1.14:30302 broadcasted 0 message(s)
Y el nodo 2 muestra el siguiente mensaje:
I0412 10:11:58.480153 12478 peer.go:173] Peer e0b2addf8107866c 192.168.1.14:35257 broadcasted 0 message(s)
Cuando se crean las reglas de iptables para bloquear los puertos P2P, las conexiones P2P entre las instancias de geth se interrumpen después de aproximadamente 1 minuto.
El nodo 1 muestra los siguientes mensajes:
I0412 10:12:59.080325 12467 server.go:431] new task: static dial 3941d48d95d4782f 192.168.1.14:30302
I0412 10:12:59.080407 12467 dial.go:209] dialing enode://3941d48d95d4782f8b4fb7561d78642d2e53e478e5c8d3087e6e6023f5931aca3024a9679628fff775b9ddafd11d8d48f84a502fe815619be80f16b54cb1c077@192.168.1.14:30302
I0412 10:13:14.080977 12467 dial.go:212] dial error: dial tcp 192.168.1.14:30302: i/o timeout
> admin.peers
[]
Y el nodo 2 muestra los siguientes mensajes:
I0412 10:20:28.784346 12478 server.go:431] new task: static dial e0b2addf8107866c 192.168.1.14:30301
I0412 10:12:58.780403 12478 dial.go:209] dialing enode://e0b2addf8107866c0e33a56f51cf800f2625ea0f4f70097ce6420b941d215c55c5404bb856d94911964865e8ac640b7ce2a2426afbf01d16d85e8f26f053a070@192.168.1.14:30301
I0412 10:13:13.780693 12478 dial.go:212] dial error: dial tcp 192.168.1.14:30301: i/o timeout
> admin.peers
[]
El tenedor
Cuando la conexión P2P está bloqueada, la cadena de bloques se bifurca con la primera instancia de geth minando en su copia separada de la cadena de bloques, y la segunda instancia de geth minando en su copia separada de la cadena de bloques.
A continuación, verá las bifurcaciones de la cadena de bloques en el bloque n.º 1405. Creé una secuencia de checkBlock()
comandos que aparece en la parte inferior de esta página: la columna de la izquierda muestra el número de bloque y la columna de la derecha muestra los primeros 4 caracteres de la dirección de la base de monedas del minero.
Ejecutar el comando checkBlock(1401, 10000)
en la primera instancia de geth produce el siguiente resultado:
1401 182434 0 Infinity 0 0 NaN 909f
1402 182523 89 364957.0 1 1 1.0 909f
1403 182612 178 273784.5 9 8 4.5 909f
1404 182523 89 243364.0 41 32 13.7 8d15
1405 182612 178 228176.0 51 10 12.8 8d15 <--- THE FORK
1406 182523 89 219045.4 69 18 13.8 8d15
1407 182612 178 212973.2 78 9 13.0 8d15
1408 182701 267 208648.6 90 12 12.9 8d15
1409 182612 178 205394.0 117 27 14.6 8d15
1410 182701 267 202872.6 127 10 14.1 8d15
Ejecutar el comando checkBlock(1390, 10000)
en la segunda instancia de geth produce los siguientes resultados:
1401 182434 0 Infinity 0 0 NaN 909f
1402 182523 89 364957.0 1 1 1.0 909f
1403 182612 178 273784.5 9 8 4.5 909f
1404 182523 89 243364.0 41 32 13.7 8d15
1405 182612 178 228176.0 51 10 12.8 909f <--- THE FORK
1406 182523 89 219045.4 87 36 17.4 909f
1407 182434 0 212943.5 135 48 22.5 909f
1408 182523 89 208597.7 145 10 20.7 909f
1409 182434 0 205327.2 192 47 24.0 909f
1410 182345 -89 202773.7 237 45 26.3 909f
Gasto de la misma cuenta durante la bifurcación
En ambas instancias de geth, tenía la segunda cuenta eth.accounts[1]
configurada con la misma clave pública/privada.
Antes de la bifurcación, los saldos de las cuentas eran los mismos en ambas instancias de geth:
web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
10.09958
En la primera instancia de geth después de la bifurcación, transfirí 7 éteres de eth.accounts[1]
a eth.accounts[0]
.
> eth.sendTransaction({from: eth.accounts[1], to: eth.accounts[0], value: web3.toWei(7, "ether")})
"0xe442a4e325ff6be2fbb35ba1f381e56559df722ebc307c6ad57f3580d6b97412"
...
> web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
3.09916
En la segunda instancia de geth después de la bifurcación, transfirí 6 éteres de eth.accounts[1]
a eth.accounts[0]
.
> eth.sendTransaction({from: eth.accounts[1], to: eth.accounts[0], value: web3.toWei(6, "ether")})
"0xa06a6a141f5ab19e0be266244eb6c07c5b9bece6dc308f5fd6244dee51606fa6"
...
> web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
4.09916
Después de desbloquear el puerto P2P, ambas instancias de geth sincronizaron sus cadenas de bloques (presumiblemente con la cadena de dificultad más larga/más alta) con el resultado de que ambas instancias de geth informaron un saldo de 4.09916 en eth.accounts[1]
.
¿Se ajustó la dificultad hacia abajo cuando la cadena de bloques se bifurcó?
Sí, en el caso de la segunda instancia de geth, como se puede ver en el bloque #1410 a continuación:
> checkBlocks(1401,10000);
1401 182434 0 Infinity 0 0 NaN 909f
1402 182523 89 364957.0 1 1 1.0 909f
1403 182612 178 273784.5 9 8 4.5 909f
1404 182523 89 243364.0 41 32 13.7 8d15
1405 182612 178 228176.0 51 10 12.8 909f
1406 182523 89 219045.4 87 36 17.4 909f
1407 182434 0 212943.5 135 48 22.5 909f
1408 182523 89 208597.7 145 10 20.7 909f
1409 182434 0 205327.2 192 47 24.0 909f
1410 182345 -89 202773.7 237 45 26.3 909f
1411 182256 -178 200721.9 275 38 27.5 909f
1412 182168 -266 199035.2 306 31 27.8 909f
1413 182256 -178 197636.9 313 7 26.1 909f
1414 182168 -266 196447.0 358 45 27.5 909f
1415 182256 -178 195433.4 366 8 26.1 909f
1416 182168 -266 194549.0 388 22 25.9 909f
1417 182256 -178 193780.7 397 9 24.8 909f
1418 182344 -90 193107.9 407 10 23.9 909f
1419 182433 -1 192514.9 418 11 23.2 909f
1420 182344 -90 191979.6 465 47 24.5 909f
1421 182255 -179 191493.4 491 26 24.6 909f
Esperaba dificultades para ajustar hacia abajo porque la cadena de bloques combinada tenía dos mineros, mientras que la cadena de bloques bifurcada individual estaba siendo construida por un minero. Para mantener el mismo tiempo entre bloques (en promedio), la dificultad tendría que ajustarse hacia abajo.
Cosas Adicionales
~/ForkIt/etc/CustomGenesis.json
{
"alloc": {
},
"nonce": "0x8888888888888888",
"difficulty": "0x020000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x8888888888888888888888888888888888888888",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x888888"
}
secuencia de comandos checkBlocks()
Para el rango especificado de bloques, este script imprimirá la siguiente información: número de bloque, dificultad del bloque, cambio en la dificultad del bloque, dificultad promedio del bloque, tiempo transcurrido, cambio en el tiempo, tiempo promedio y los primeros 4 caracteres de la clave pública de la base de monedas del minero. .
function checkBlocks(firstBlock, lastBlock) {
var i;
var firstTimestamp;
var prevTimestamp;
var prevDifficulty;
var totalDifficulty = 0;
for (i = firstBlock; i < 10000; i++) {
var block = eth.getBlock(i);
if (i == firstBlock) {
firstTimestamp = block.timestamp;
prevTimestamp = firstTimestamp;
prevDifficulty = block.difficulty;
}
if (block == null)
break;
totalDifficulty = +totalDifficulty + +block.difficulty;
var averageDifficulty = totalDifficulty / (i - firstBlock);
var averageTime = (block.timestamp - firstTimestamp) / (i - firstBlock);
console.log(block.number + "\t" + block.difficulty +
"\t" + (block.difficulty - prevDifficulty) +
"\t" + averageDifficulty.toFixed(1) +
"\t" + (block.timestamp - firstTimestamp) +
"\t" + (block.timestamp - prevTimestamp) +
"\t" + averageTime.toFixed(1) +
"\t" + block.miner.substr(2, 4));
prevTimestamp = block.timestamp;
}
}
Los resultados se parecen a los siguientes (la información de diferencia de la primera línea siempre será incorrecta):
> checkBlocks(1401,10000);
1401 182434 0 Infinity 0 0 NaN 909f
1402 182523 89 364957.0 1 1 1.0 909f
1403 182612 178 273784.5 9 8 4.5 909f
1404 182523 89 243364.0 41 32 13.7 8d15
1405 182612 178 228176.0 51 10 12.8 8d15
1406 182523 89 219045.4 69 18 13.8 8d15
1407 182612 178 212973.2 78 9 13.0 8d15
1408 182701 267 208648.6 90 12 12.9 8d15
1409 182612 178 205394.0 117 27 14.6 8d15
privacidadisahumanright.eth
pablo s