Я хотел бы надежно вызвать форк в приватной тестовой сети, чтобы проверить поведение внеблокчейнового кода, взаимодействующего с контрактами. (см. связанные вопросы о том, почему я хотел бы это сделать). Вероятно, будет полезно для автоматического тестирования реализации geth и других узлов.
Под надежным я подразумеваю воспроизводимый, каждый раз с одним и тем же результатом, так что я могу запустить это в автоматизированной тестовой системе.
Аналогичный вопрос появился на форумах Эфириума.
Решение
admin.addPeer(...)
команда для добавления одноранговых узлов в список одноранговых узлов geth, но, похоже, нет команд для удаления этих одноранговых узлов.Детали теста следующие:
Тест
Первый экземпляр geth использует порт P2P 30301 со следующей командой:
user@Kumquat:~/ForkIt$ geth --datadir ./data1 \
--genesis ~/ForkIt/etc/CustomGenesis.json \
--networkid 8888 --nodiscover --mine --minerthreads 1 \
--port 30301 --maxpeers 10 console
Второй экземпляр geth использует порт P2P 30302 со следующей командой:
user@Kumquat:~/ForkIt$ geth --datadir ./data2 \
--genesis ~/ForkIt/etc/CustomGenesis.json \
--networkid 8888 --nodiscover --mine --minerthreads 1 \
--port 30302 --maxpeers 10 console
Первый экземпляр geth использует файл ./data1/static-nodes.json
для поиска второго экземпляра geth. Этот файл содержит информацию об эноде, полученную с помощью admin.nodeInfo
команды из второго экземпляра geth, где я заменил текст [::] на IP-адрес моего компьютера. Вот как ./data1/static-nodes.json
выглядит мой:
[
"enode://3941d48d95d4782f8b4fb7561d78642d2e53e478e5c8d3087e6e6023f5931aca3024a9679628fff775b9ddafd11d8d48f84a502fe815619be80f16b54cb1c077@192.168.1.14:30302"
]
Второй экземпляр geth использует файл ./data2/static-nodes.json
с информацией об эноде, полученный с помощью admin.nodeInfo
команды из первого экземпляра geth.
Чтобы имитировать форк, я блокирую TCP-порты 30301 и 30302, используя следующие команды:
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
Чтобы экземпляры geth могли повторно подключаться как одноранговые узлы, я удаляю правила блокировки на TCP-портах 30301 и 30302 с помощью следующих команд:
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
Подключение и отключение P2P
Чтобы просмотреть трассировку P2P-соединения между экземплярами geth, выполните команду admin.verbosity(6)
в консоли geth.
Когда порты разблокированы
Узел 1 показывает следующее сообщение:
I0412 10:11:47.379824 12467 peer.go:173] Peer 3941d48d95d4782f 192.168.1.14:30302 broadcasted 0 message(s)
И узел 2 показывает следующее сообщение:
I0412 10:11:58.480153 12478 peer.go:173] Peer e0b2addf8107866c 192.168.1.14:35257 broadcasted 0 message(s)
Когда создаются правила iptables для блокировки портов P2P, соединения P2P между экземплярами geth разрываются примерно через 1 минуту.
Узел 1 показывает следующие сообщения:
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
[]
И узел 2 показывает следующие сообщения:
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
[]
Разветвление
Когда P2P-соединение заблокировано, блок-цепочка разветвляется, при этом первый экземпляр geth выполняет майнинг в своей отдельной копии блокчейна, а второй экземпляр geth — в своей отдельной копии блокчейна.
Ниже вы увидите форки блокчейна на блоке №1405. Я создал checkBlock()
скрипт, который указан внизу этой страницы — в левом столбце показан номер блока, а в правом столбце показаны первые 4 символа адреса майнера в монетной базе.
Выполнение команды checkBlock(1401, 10000)
в первом экземпляре geth дает следующий результат:
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
Выполнение команды checkBlock(1390, 10000)
во втором экземпляре geth дает следующие результаты:
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
Расходы с одного и того же счета во время форка
В обоих экземплярах geth у меня была вторая учетная запись eth.accounts[1]
, настроенная на один и тот же закрытый/открытый ключ.
До форка балансы счетов были одинаковыми для обоих экземпляров geth:
web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
10.09958
В первом экземпляре гета после форка я перевел 7 эфиров с eth.accounts[1]
на 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
Во втором инстансе geth после форка я перевел 6 эфиров с eth.accounts[1]
на 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
После того, как я разблокировал порт P2P, оба экземпляра geth синхронизировали свои блокчейны (предположительно, с самой длинной/самой сложной цепочкой), в результате чего оба экземпляра geth сообщили о балансе 4,09916 в eth.accounts[1]
.
Изменилась ли сложность вниз при разветвлении блокчейна?
Да, в случае второго экземпляра geth, как видно из блока № 1410 ниже:
> 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
Я ожидал, что трудности снизятся, потому что в объединенной цепочке блоков было два майнера, тогда как индивидуальная разветвленная цепочка блоков создавалась одним майнером. Чтобы время между блоками оставалось одинаковым (в среднем), сложность должна быть снижена.
Дополнительные вещи
~/ForkIt/etc/CustomGenesis.json
{
"alloc": {
},
"nonce": "0x8888888888888888",
"difficulty": "0x020000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x8888888888888888888888888888888888888888",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x888888"
}
Скрипт checkBlocks()
Для указанного диапазона блоков этот скрипт напечатает следующую информацию: номер блока, сложность блока, изменение сложности блока, средняя сложность блока, прошедшее время, изменение времени, среднее время и первые 4 символа открытого ключа майнера coinbase. .
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;
}
}
Результаты выглядят следующим образом (информация о разнице в первой строке всегда будет неверной):
> 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
конфиденциальностьisahumanright.eth
Пол С