Core-GethThe pages of this site follow this link

Core-Geth v1.12 peer discovery

Keep a v1.12 node finding peers until it moves

Every node still on a v1.12 release should move to the v1.13 line. Migrate Core-Geth has the steps. Until a node can move, this guide points it at the peer discovery lists and bootnodes the v1.13 line ships, so it keeps finding peers after the lists a v1.12 release reads stop being updated. The node stays vulnerable: this closes none of the flaws the audits found in the v1.12 line.

It also shows how to start a node on either line from the node records committed in ethereumclassic/discv4-dns-lists, if the lists and bootnodes ever go dark.

Core-Geth v1.12 peer discovery at a glance

What this changes, and what it does not

  • The lists change. The node reads the three DNS discovery lists ethereumclassic/discv4-dns-lists publishes for its network, the ones the v1.13 line reads by default, in place of the one its release reads.
  • The bootnodes change. It starts from the bootnodes the v1.13 line ships, which the Core-Geth maintainers run across hosting providers and countries, in place of the ones compiled into its release.
  • The release stays. The node goes on running its v1.12 release, with every flaw the audits found in it. Migrate Core-Geth moves it to the v1.13 line, which reads these lists and bootnodes with no flags at all.
  • The chain data and the key stay. Nothing resyncs, and the P2P node key does not change. The node is down only while you stop it, change how it starts and start it again; copying a container's data out takes longer.

Before changing a Core-Geth v1.12 node

Check how the node starts

Find how the node starts: the command you run in a terminal, the ExecStart line of a systemd unit, the ProgramArguments of a launchd agent, the command file in the Windows Startup folder, or a container's flags, which docker inspect <container> --format '{{json .Args}}' prints. The steps add two flags there.

  • Its release. Ask the running node for its name with geth attach --exec 'admin.nodeInfo.name' and the node's IPC path. These steps are for v1.12.17 and later: a release before v1.12.17 does not schedule the Spiral upgrade, so a node on one has left Ethereum Classic, and the lists carry only nodes on the network's current fork. Migrate Core-Geth says how such a node resyncs.
  • A config file. If the node starts with --config, the file can name the lists and bootnodes the node reads, and the steps change it too.
  • A build from source. A node built from source starts the same way as a download, so use the tab for its platform.

Changing a Core-Geth v1.12 node's peer discovery

Point the node at the v1.13 line's lists and bootnodes

Run one block at a time, in order, and read the note under each before the next. In the Linux tab, the clean stop and the start on the new lists carry recordings, made on v1.12.20. Using the trees in ethereumclassic/discv4-dns-lists gives the same flags and file.

Choose your platform for changing a node's peer discovery

Linux

Record the node's block number

geth attach --exec 'eth.blockNumber' ~/.ethereum/classic/geth.ipc

Keep what this prints: "Confirm the node holds peers" compares the block number with it. The command uses the default data directory. For Mordor, use ~/.ethereum/mordor; if you pass --datadir, use yours. On the unit in the Linux run guide, the geth user owns /var/lib/core-geth, so attach as sudo -u geth -H geth --classic attach --exec 'eth.blockNumber' /var/lib/core-geth/geth.ipc instead, here and in every later attach line.

Stop the node cleanly

Press Ctrl-C once in the node's terminal and wait, or stop the systemd unit you wrote, for example sudo systemctl stop core-geth. Wait until the log prints Blockchain stopped and the process exits. If it exits without that line, the stop was not clean: the node recovers at its next start, and its block number can start lower than the one you recorded. Under systemd, journalctl -u core-geth shows that log.

Recorded on Linux, on v1.12.20One Ctrl-C in the node's terminal: the node writes its state to disk and logs Blockchain stopped.

If the node starts from a config file, update it

If the node starts with --config, edit that file while the node is down. Under [Eth], put the three lists in place of the values of these two lines:

[Eth]
EthDiscoveryURLs = [
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network",
]
SnapDiscoveryURLs = [
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network",
]

Under [Node.P2P], delete any BootstrapNodes and BootstrapNodesV5 lines: they hold the v1.12 release's bootnodes, and the --bootnodes flag in the next step takes their place. Leave --discovery.dns off the start line: it sets only EthDiscoveryURLs, and the file now sets both. For Mordor, use the three all.mordor. lists. Using the trees shows the same file.

Start it with the v1.13 line's lists and bootnodes

From a terminal, with the node's other flags added to this line: a node on --datadir that runs it as it stands starts an empty chain in the default directory.

geth --classic \
  --discovery.dns "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network" \
  --bootnodes "enode://5c1f4f1c8036879c3186f0632ce0fdf3514e67679f0b5e1814335f5093a21c6c2fda20266ca2b54aac3305085fa46d9711493da1b564489ecec7b37d1aebb5f6@178.104.220.119:30303,enode://82100619a51fed73bce973a5f7ead3a9468535bdd4c87488037f99bf53b741e4701c5909b7c92897c863c23e494e27c83470a2fcbcc51f8b49655ed377dbd962@135.148.46.64:30303,enode://d77bc6696fc2a1419871c8819baef44089cd1847b1b895266382892fd04571cc01a97c45d4df6cbe4c37004195a3c578a76e706d12548916ca5c4341768b1972@62.72.47.101:30303"

Under systemd, put the two flags and their values on the unit's ExecStart line instead, then run sudo systemctl daemon-reload && sudo systemctl start core-geth, where core-geth stands for your unit. If the line already has --discovery.dns or --bootnodes, put these values in place of theirs. If you updated a config file, leave out --discovery.dns and its value. The node then reads these three lists in place of the one its release reads, and starts from the v1.13 line's bootnodes in place of its own. It needs no working bootnode to use the lists, because it dials the nodes in them directly.

Recorded on Linux, on v1.12.20Started from a terminal with the step's line and --syncmode full, the node's other flag. It advertises 203.0.113.10, an address reserved for documentation, set with GETH_NAT for this recording in place of the node's own, and logs Block synchronisation started 8 seconds after its P2P networking starts, then imports blocks.

On Mordor, start it with this line instead

Mordor, Ethereum Classic's test network, has lists and bootnodes of its own:

geth --mordor \
  --discovery.dns "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethereumclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethereumclassic.network" \
  --bootnodes "enode://d6f2e535a2d183f930bd780913d1d4169d3479f22c2941117d10184d65adbb614866db83049438c17657d1b24a53f3560280f563305426e93868a9a98a02c5fc@2.29.31.123:30303,enode://b0b8da5708a22a0bc92e993c6fb93801d1bc5956efaf3ef7cafcd3af2167f634a2147abb6913a719d61ed03e180aa2045ffea7bd2e367db589165e83058df487@40.160.140.180:30303"

The notes under "Start it with the v1.13 line's lists and bootnodes" apply to this one too.

Confirm the node holds peers

In another terminal:

geth attach --exec 'net.peerCount' ~/.ethereum/classic/geth.ipc
geth attach --exec 'eth.blockNumber' ~/.ethereum/classic/geth.ipc

If an attach line cannot connect, wait a minute and run it again. The peer count climbs within minutes. If it is still zero after five minutes, see Why does the node have no peers?, and if the lists and bootnodes have gone dark, start it once from the node records. The block number starts near the one you recorded, or lower after an unclean stop, and climbs to the network's head.

macOS

Record the node's block number

geth attach --exec 'eth.blockNumber' ~/Library/Ethereum/classic/geth.ipc

Keep what this prints: "Confirm the node holds peers" compares the block number with it. The command uses the default data directory. For Mordor, use ~/Library/Ethereum/mordor; if you pass --datadir, use yours.

Stop the node cleanly

Press Ctrl-C once in the node's terminal and wait, or unload the launchd job. Stop it cleanly has the command for the run guide's agent. Wait until the log prints Blockchain stopped and the process exits. If it exits without that line, the stop was not clean: the node recovers at its next start, and its block number can start lower than the one you recorded.

If the node starts from a config file, update it

If the node starts with --config, edit that file while the node is down. Under [Eth], put the three lists in place of the values of these two lines:

[Eth]
EthDiscoveryURLs = [
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network",
]
SnapDiscoveryURLs = [
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network",
]

Under [Node.P2P], delete any BootstrapNodes and BootstrapNodesV5 lines: they hold the v1.12 release's bootnodes, and the --bootnodes flag in the next step takes their place. Leave --discovery.dns off the start line: it sets only EthDiscoveryURLs, and the file now sets both. For Mordor, use the three all.mordor. lists. Using the trees shows the same file.

Start it with the v1.13 line's lists and bootnodes

From a terminal, with the node's other flags added to this line: a node on --datadir that runs it as it stands starts an empty chain in the default directory.

geth --classic \
  --discovery.dns "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network" \
  --bootnodes "enode://5c1f4f1c8036879c3186f0632ce0fdf3514e67679f0b5e1814335f5093a21c6c2fda20266ca2b54aac3305085fa46d9711493da1b564489ecec7b37d1aebb5f6@178.104.220.119:30303,enode://82100619a51fed73bce973a5f7ead3a9468535bdd4c87488037f99bf53b741e4701c5909b7c92897c863c23e494e27c83470a2fcbcc51f8b49655ed377dbd962@135.148.46.64:30303,enode://d77bc6696fc2a1419871c8819baef44089cd1847b1b895266382892fd04571cc01a97c45d4df6cbe4c37004195a3c578a76e706d12548916ca5c4341768b1972@62.72.47.101:30303"

If the node runs under the run guide's launchd agent, add the two flags and their values to the agent's ProgramArguments instead, each as a <string> of its own, and start the agent as Keep it running shows. If the line already has --discovery.dns or --bootnodes, put these values in place of theirs. If you updated a config file, leave out --discovery.dns and its value. The node then reads these three lists in place of the one its release reads, and starts from the v1.13 line's bootnodes in place of its own. It needs no working bootnode to use the lists, because it dials the nodes in them directly.

On Mordor, start it with this line instead

Mordor, Ethereum Classic's test network, has lists and bootnodes of its own:

geth --mordor \
  --discovery.dns "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethereumclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethereumclassic.network" \
  --bootnodes "enode://d6f2e535a2d183f930bd780913d1d4169d3479f22c2941117d10184d65adbb614866db83049438c17657d1b24a53f3560280f563305426e93868a9a98a02c5fc@2.29.31.123:30303,enode://b0b8da5708a22a0bc92e993c6fb93801d1bc5956efaf3ef7cafcd3af2167f634a2147abb6913a719d61ed03e180aa2045ffea7bd2e367db589165e83058df487@40.160.140.180:30303"

The notes under "Start it with the v1.13 line's lists and bootnodes" apply to this one too.

Confirm the node holds peers

In another terminal:

geth attach --exec 'net.peerCount' ~/Library/Ethereum/classic/geth.ipc
geth attach --exec 'eth.blockNumber' ~/Library/Ethereum/classic/geth.ipc

If an attach line cannot connect, wait a minute and run it again. The peer count climbs within minutes. If it is still zero after five minutes, see Why does the node have no peers?, and if the lists and bootnodes have gone dark, start it once from the node records. The block number starts near the one you recorded, or lower after an unclean stop, and climbs to the network's head.

Windows

Record the node's block number

In PowerShell, in the folder that holds the node's geth.exe:

.\geth.exe attach --exec 'eth.blockNumber' \\.\pipe\geth.ipc

Keep what this prints: "Confirm the node holds peers" compares the block number with it.

Stop the node cleanly

Press Ctrl+C once in the node's console window and wait. A window opened from the Startup folder then asks Terminate batch job (Y/N)?; type Y. Under a service wrapper, stop it with that tool. Wait until the log prints Blockchain stopped and the process exits. If it exits without that line, the stop was not clean: the node recovers at its next start, and its block number can start lower than the one you recorded.

If the node starts from a config file, update it

If the node starts with --config, edit that file while the node is down. Under [Eth], put the three lists in place of the values of these two lines:

[Eth]
EthDiscoveryURLs = [
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network",
]
SnapDiscoveryURLs = [
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network",
]

Under [Node.P2P], delete any BootstrapNodes and BootstrapNodesV5 lines: they hold the v1.12 release's bootnodes, and the --bootnodes flag in the next step takes their place. Leave --discovery.dns off the start line: it sets only EthDiscoveryURLs, and the file now sets both. For Mordor, use the three all.mordor. lists. Using the trees shows the same file. Save the file as UTF-8 with no byte order mark. A byte order mark, or UTF-16, stops the start with invalid TOML syntax.

Start it with the v1.13 line's lists and bootnodes

In PowerShell, in the folder that holds the node's geth.exe, with the node's other flags added to this line: a node on --datadir that runs it as it stands starts an empty chain in the default directory.

.\geth.exe --classic --discovery.dns "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network" --bootnodes "enode://5c1f4f1c8036879c3186f0632ce0fdf3514e67679f0b5e1814335f5093a21c6c2fda20266ca2b54aac3305085fa46d9711493da1b564489ecec7b37d1aebb5f6@178.104.220.119:30303,enode://82100619a51fed73bce973a5f7ead3a9468535bdd4c87488037f99bf53b741e4701c5909b7c92897c863c23e494e27c83470a2fcbcc51f8b49655ed377dbd962@135.148.46.64:30303,enode://d77bc6696fc2a1419871c8819baef44089cd1847b1b895266382892fd04571cc01a97c45d4df6cbe4c37004195a3c578a76e706d12548916ca5c4341768b1972@62.72.47.101:30303"

If the node starts from the Startup folder, as the run guide's Keep it running sets up, put the two flags and their values on the line in that command file instead, and run it. If the line already has --discovery.dns or --bootnodes, put these values in place of theirs. If you updated a config file, leave out --discovery.dns and its value. The node then reads these three lists in place of the one its release reads, and starts from the v1.13 line's bootnodes in place of its own. It needs no working bootnode to use the lists, because it dials the nodes in them directly.

On Mordor, start it with this line instead

Mordor, Ethereum Classic's test network, has lists and bootnodes of its own:

.\geth.exe --mordor --discovery.dns "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethereumclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethereumclassic.network" --bootnodes "enode://d6f2e535a2d183f930bd780913d1d4169d3479f22c2941117d10184d65adbb614866db83049438c17657d1b24a53f3560280f563305426e93868a9a98a02c5fc@2.29.31.123:30303,enode://b0b8da5708a22a0bc92e993c6fb93801d1bc5956efaf3ef7cafcd3af2167f634a2147abb6913a719d61ed03e180aa2045ffea7bd2e367db589165e83058df487@40.160.140.180:30303"

The notes under "Start it with the v1.13 line's lists and bootnodes" apply to this one too.

Confirm the node holds peers

In a second PowerShell window, in the same folder:

.\geth.exe attach --exec 'net.peerCount' \\.\pipe\geth.ipc
.\geth.exe attach --exec 'eth.blockNumber' \\.\pipe\geth.ipc

If an attach line cannot connect, wait a minute and run it again. The peer count climbs within minutes. If it is still zero after five minutes, see Why does the node have no peers?, and if the lists and bootnodes have gone dark, start it once from the node records. The block number starts near the one you recorded, or lower after an unclean stop, and climbs to the network's head.

Containers

Record the node's block number, image and restart policy

docker exec core-geth geth attach --exec 'eth.blockNumber' /root/.ethereum/classic/geth.ipc
docker inspect core-geth --format '{{.Config.Image}}'
docker inspect core-geth --format '{{.HostConfig.RestartPolicy.Name}}'

Keep what these print: "Confirm the node holds peers" compares the block number, the new container runs the same v1.12 image, and it takes the old container's restart policy. These commands use a container named core-geth with its data at /root, so put your own name in its place. For Mordor, use .ethereum/mordor in the path.

Check where the container keeps its data

docker inspect core-geth --format '{{range .Mounts}}{{.Destination}} {{end}}'

This prints /root, or /root/.ethereum, when the data is in a host directory or a named volume, which the new container takes over. If it prints neither, the data is inside the container itself: after the stop, take the copy step below, or the new container starts with no chain and resyncs.

Stop the node cleanly

docker stop -t 300 core-geth
docker logs --tail 50 core-geth 2>&1 | grep 'Blockchain stopped'

The second command prints Blockchain stopped after a clean stop. If it prints nothing, the node did not stop cleanly: it recovers at its next start, moving its head back and importing those blocks again (After an unclean stop), so its block number can start lower than the one you recorded. Use -t 300: a plain docker stop kills the node if it has not stopped within 10 seconds.

If the data is inside the container, copy it out

If the check printed neither, run this. It copies the data out of the stopped container, which needs as much free space as the chain takes:

mkdir -p $HOME/core-geth-data &&
  docker cp core-geth:/root/. $HOME/core-geth-data/

The chain comes with the copy, so the node does not resync.

If the node starts from a config file, update it

If the node starts with --config, edit that file while the node is down. Under [Eth], put the three lists in place of the values of these two lines:

[Eth]
EthDiscoveryURLs = [
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network",
]
SnapDiscoveryURLs = [
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net",
  "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network",
]

Under [Node.P2P], delete any BootstrapNodes and BootstrapNodesV5 lines: they hold the v1.12 release's bootnodes, and the --bootnodes flag in the next step takes their place. Leave --discovery.dns off the start line: it sets only EthDiscoveryURLs, and the file now sets both. For Mordor, use the three all.mordor. lists. Using the trees shows the same file. For a container, edit the file its --config names, on the host it is mounted from.

Replace the container with one that reads the v1.13 line's lists and bootnodes

A container cannot take a new flag, so this block replaces it with one from the same image, on the same data. Before you run it, put the old container's flags in its last lines, after --classic, and its GETH_ variables in as -e options: docker inspect core-geth --format '{{json .Args}} {{json .Config.Env}}' prints both. Keep its port mappings, and put the restart policy you recorded in place of unless-stopped. If the old container ran with --network host, run this one that way too, without the -p options and with --http.addr 127.0.0.1. If you copied the data out, put -v $HOME/core-geth-data:/root in place of --volumes-from core-geth-old.

IMAGE=$(docker inspect core-geth --format '{{.Config.Image}}') &&
  docker rename core-geth core-geth-old &&
  docker update --restart no core-geth-old &&
  docker run -d --name core-geth \
    --restart unless-stopped --stop-timeout 300 \
    --volumes-from core-geth-old \
    -p 30303:30303 -p 30303:30303/udp -p 127.0.0.1:8545:8545 \
    "$IMAGE" \
    --classic --http --http.addr 0.0.0.0 --http.port 8545 \
    --discovery.dns "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.classic.ethereumclassic.network" \
    --bootnodes "enode://5c1f4f1c8036879c3186f0632ce0fdf3514e67679f0b5e1814335f5093a21c6c2fda20266ca2b54aac3305085fa46d9711493da1b564489ecec7b37d1aebb5f6@178.104.220.119:30303,enode://82100619a51fed73bce973a5f7ead3a9468535bdd4c87488037f99bf53b741e4701c5909b7c92897c863c23e494e27c83470a2fcbcc51f8b49655ed377dbd962@135.148.46.64:30303,enode://d77bc6696fc2a1419871c8819baef44089cd1847b1b895266382892fd04571cc01a97c45d4df6cbe4c37004195a3c578a76e706d12548916ca5c4341768b1972@62.72.47.101:30303"

If the old container's command already has --discovery.dns or --bootnodes, put these values in place of theirs. If you updated a config file, leave out --discovery.dns and its value. The node then reads these three lists in place of the one its release reads, and starts from the v1.13 line's bootnodes in place of its own. It needs no working bootnode to use the lists, because it dials the nodes in them directly.

On Mordor, run this block instead

Mordor, Ethereum Classic's test network, has lists and bootnodes of its own:

IMAGE=$(docker inspect core-geth --format '{{.Config.Image}}') &&
  docker rename core-geth core-geth-old &&
  docker update --restart no core-geth-old &&
  docker run -d --name core-geth \
    --restart unless-stopped --stop-timeout 300 \
    --volumes-from core-geth-old \
    -p 30303:30303 -p 30303:30303/udp -p 127.0.0.1:8545:8545 \
    "$IMAGE" \
    --mordor --http --http.addr 0.0.0.0 --http.port 8545 \
    --discovery.dns "enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethereumclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethclassic.net,enrtree://APDLRZ2T7ERXPWXX4D5USB32NIFYHXMFVZQ3DZALK6JJJ5L4VSYIQ@all.mordor.ethereumclassic.network" \
    --bootnodes "enode://d6f2e535a2d183f930bd780913d1d4169d3479f22c2941117d10184d65adbb614866db83049438c17657d1b24a53f3560280f563305426e93868a9a98a02c5fc@2.29.31.123:30303,enode://b0b8da5708a22a0bc92e993c6fb93801d1bc5956efaf3ef7cafcd3af2167f634a2147abb6913a719d61ed03e180aa2045ffea7bd2e367db589165e83058df487@40.160.140.180:30303"

The notes under "Replace the container with one that reads the v1.13 line's lists and bootnodes" apply to this one too.

Confirm the node holds peers

For Mordor, use .ethereum/mordor in the paths:

docker exec core-geth geth attach --exec 'net.peerCount' /root/.ethereum/classic/geth.ipc
docker exec core-geth geth attach --exec 'eth.blockNumber' /root/.ethereum/classic/geth.ipc

If an attach line cannot connect, wait a minute and run it again. The peer count climbs within minutes. If it is still zero after five minutes, see Why does the node have no peers?, and if the lists and bootnodes have gone dark, start it once from the node records. The block number starts near the one you recorded, or lower after an unclean stop, and climbs to the network's head. The old container stays, stopped, as core-geth-old. Once the new one holds peers, remove it with docker rm core-geth-old, which leaves the volumes the new container uses.

Starting a Core-Geth node without the lists

If the lists and bootnodes go dark

Each list is committed in ethereumclassic/discv4-dns-lists exactly as it was published, so its node records stay available from that repository whether DNS answers or not. A node that is already running keeps its peers and goes on finding more through the discovery network, and the bootnodes the v1.13 line ships, which this guide's steps give a v1.12 node too, do not use DNS at all. A node on either line that still cannot find peers can start once from the committed records, then start the usual way again. DNS hosting and outages explains how the lists are served.

ethereumclassic/discv4-dns-lists reports each night, on its pipeline-status issue, whether every bootnode Core-Geth ships answered the crawl. Subscribe to that issue to be told when a bootnode stops answering. In the Linux tab, the start from the records carries a recording, made on v1.12.20.

Choose your platform for starting a node from the committed records

Linux

Check that curl and jq are installed

curl --version
jq --version

The node can keep running for this step. Each line must print a version. If jq --version prints command not found, install jq with your package manager, such as sudo apt-get install jq on Debian and Ubuntu or sudo dnf install jq on Fedora.

Start the node once from the committed records

Stop the node cleanly first: press Ctrl-C once in its terminal, or stop its systemd unit, and wait for Blockchain stopped. Then, from a terminal, with the node's other flags added to this line: a node on --datadir that runs it as it stands starts an empty chain in the default directory.

geth --classic --bootnodes "$(curl -fsSL https://raw.githubusercontent.com/ethereumclassic/discv4-dns-lists/main/all.classic.ethereumclassic.net/nodes.json \
  | jq -r '[to_entries | sort_by(.value.score, .value.lastResponse) | reverse | .[:20][] | .value.record] | join(",")')"

It takes up to twenty Ethereum Classic nodes with the highest scores, the ones that have kept answering the nightly crawl, and passes them to the node as bootnodes. Every v1.12 and v1.13 release accepts these enr: records as bootnodes. The flag replaces the built-in list, and a malformed entry stops the node from starting, so pass the list as the command produces it. Use it for the start that needs it, then take it out: in place of the built-in bootnodes it keeps twenty nodes that age from the day you ran it. On a node that starts with --bootnodes, as this guide's steps have a v1.12 node do, it takes that flag's place for this start. For Mordor, use --mordor, and all.mordor.ethereumclassic.net in place of all.classic.ethereumclassic.net in the address. If GitHub cannot be reached, read the same file from a clone of ethereumclassic/discv4-dns-lists. Under systemd, a unit cannot run the $(...) part, so stop the unit and run this line from a terminal, with the unit's flags, as the user the unit runs as: for the unit in the Linux run guide, begin it with sudo -u geth -H geth --classic --datadir /var/lib/core-geth in place of geth --classic.

Recorded on Linux, on v1.12.20One Ctrl-C stops the node, which logs Blockchain stopped. The step's command, with --syncmode full added, then starts it from the twenty node records committed in ethereumclassic/discv4-dns-lists. It advertises 203.0.113.10, set with GETH_NAT for this recording, and logs Block synchronisation started 7 seconds after its P2P networking starts.

Check that it holds peers, then start it the usual way

In another terminal:

geth attach --exec 'net.peerCount' ~/.ethereum/classic/geth.ipc

Once this prints a number above zero, the node can run on. To hand it back to its systemd unit, press Ctrl-C once in its terminal, wait for Blockchain stopped, and start the unit, which starts it without the records. For Mordor, use ~/.ethereum/mordor; if you pass --datadir, use yours, such as /var/lib/core-geth/geth.ipc for the unit in the run guide.

macOS

Check that curl and jq are installed

curl --version
jq --version

The node can keep running for this step. Each line must print a version. If jq --version prints command not found, install it with Homebrew: brew install jq.

Start the node once from the committed records

Stop the node cleanly first: press Ctrl-C once in its terminal, or unload its launchd job, and wait for Blockchain stopped. Then, from a terminal, with the node's other flags added to this line: a node on --datadir that runs it as it stands starts an empty chain in the default directory.

geth --classic --bootnodes "$(curl -fsSL https://raw.githubusercontent.com/ethereumclassic/discv4-dns-lists/main/all.classic.ethereumclassic.net/nodes.json \
  | jq -r '[to_entries | sort_by(.value.score, .value.lastResponse) | reverse | .[:20][] | .value.record] | join(",")')"

It takes up to twenty Ethereum Classic nodes with the highest scores, the ones that have kept answering the nightly crawl, and passes them to the node as bootnodes. Every v1.12 and v1.13 release accepts these enr: records as bootnodes. The flag replaces the built-in list, and a malformed entry stops the node from starting, so pass the list as the command produces it. Use it for the start that needs it, then take it out: in place of the built-in bootnodes it keeps twenty nodes that age from the day you ran it. On a node that starts with --bootnodes, as this guide's steps have a v1.12 node do, it takes that flag's place for this start. For Mordor, use --mordor, and all.mordor.ethereumclassic.net in place of all.classic.ethereumclassic.net in the address. If GitHub cannot be reached, read the same file from a clone of ethereumclassic/discv4-dns-lists. A launchd agent cannot run the $(...) part, so run this line from a terminal for that start instead.

Check that it holds peers, then start it the usual way

In another terminal:

geth attach --exec 'net.peerCount' ~/Library/Ethereum/classic/geth.ipc

Once this prints a number above zero, the node can run on. To hand it back to its launchd agent, press Ctrl-C once in its terminal, wait for Blockchain stopped, and start the agent, which starts it without the records. For Mordor, use ~/Library/Ethereum/mordor; if you pass --datadir, use yours.

Windows

Read the committed records

# In PowerShell, in the folder that holds the node's geth.exe
$nodes = (Invoke-WebRequest "https://raw.githubusercontent.com/ethereumclassic/discv4-dns-lists/main/all.classic.ethereumclassic.net/nodes.json" -UseBasicParsing).Content | ConvertFrom-Json
$records = ($nodes.PSObject.Properties.Value | Sort-Object score, lastResponse -Descending | Select-Object -First 20).record -join ","
$records.StartsWith("enr:")

The node can keep running for this step. The last line must print True. If it prints False or an error, the records were not read: do not start the node with them.

Start the node once from them

Stop the node cleanly first: press Ctrl+C once in its console window and wait for Blockchain stopped. Then, in the PowerShell window where you read the records, which holds them as $records, with the node's other flags added to this line: a node on --datadir that runs it as it stands starts an empty chain in the default directory.

.\geth.exe --classic --bootnodes $records

It takes up to twenty Ethereum Classic nodes with the highest scores, the ones that have kept answering the nightly crawl, and passes them to the node as bootnodes. Every v1.12 and v1.13 release accepts these enr: records as bootnodes. The flag replaces the built-in list, and a malformed entry stops the node from starting, so pass the list as the command produces it. Use it for the start that needs it, then take it out: in place of the built-in bootnodes it keeps twenty nodes that age from the day you ran it. On a node that starts with --bootnodes, as this guide's steps have a v1.12 node do, it takes that flag's place for this start. For Mordor, use --mordor, and all.mordor.ethereumclassic.net in place of all.classic.ethereumclassic.net in the address. If GitHub cannot be reached, read the same file from a clone of ethereumclassic/discv4-dns-lists. If the node starts from the Startup folder, run this line for that start instead.

Check that it holds peers, then start it the usual way

In a second PowerShell window, in the same folder:

.\geth.exe attach --exec 'net.peerCount' \\.\pipe\geth.ipc

Once this prints a number above zero, the node can run on. To hand it back to the Startup folder, press Ctrl+C once in its console window, wait for Blockchain stopped, and run the command file there, which starts it without the records.

Containers

Check that curl and jq are installed

curl --version
jq --version

The node can keep running for this step. Each line must print a version. These run on the host. If jq --version prints command not found, install jq with the host's package manager.

Stop the node cleanly

docker stop -t 300 core-geth

Use -t 300: a plain docker stop kills the node if it has not stopped within 10 seconds. These commands use a container named core-geth, so put your own name in its place.

Start it once from the committed records, in a container of its own

This runs the container's own image on its data, in the foreground, with the records as bootnodes. Add the container's other flags after --classic, leaving out any --bootnodes, and use its P2P port mappings. If the container keeps its data inside itself rather than in a host directory or a volume, --volumes-from cannot reach it: copy the data out first, as the Containers tab of this guide's steps shows, and put -v $HOME/core-geth-data:/root in place of --volumes-from core-geth.

docker run --rm -it --name core-geth-records --volumes-from core-geth \
  -p 30303:30303 -p 30303:30303/udp \
  "$(docker inspect core-geth --format '{{.Config.Image}}')" \
  --classic --bootnodes "$(curl -fsSL https://raw.githubusercontent.com/ethereumclassic/discv4-dns-lists/main/all.classic.ethereumclassic.net/nodes.json \
    | jq -r '[to_entries | sort_by(.value.score, .value.lastResponse) | reverse | .[:20][] | .value.record] | join(",")')"

It takes up to twenty Ethereum Classic nodes with the highest scores, the ones that have kept answering the nightly crawl, and passes them to the node as bootnodes. Every v1.12 and v1.13 release accepts these enr: records as bootnodes. The flag replaces the built-in list, and a malformed entry stops the node from starting, so pass the list as the command produces it. Use it for the start that needs it, then take it out: in place of the built-in bootnodes it keeps twenty nodes that age from the day you ran it. On a node that starts with --bootnodes, as this guide's steps have a v1.12 node do, it takes that flag's place for this start. For Mordor, use --mordor, and all.mordor.ethereumclassic.net in place of all.classic.ethereumclassic.net in the address. If GitHub cannot be reached, read the same file from a clone of ethereumclassic/discv4-dns-lists.

Check that it holds peers, then start it the usual way

In another terminal, while it runs:

docker exec core-geth-records geth attach --exec 'net.peerCount' /root/.ethereum/classic/geth.ipc

Once this prints a number above zero, press Ctrl-C once in the terminal it runs in, wait for Blockchain stopped, and start the usual container again, which starts it without the records: docker start core-geth. For Mordor, use .ethereum/mordor in the path.

Moving to the v1.13 line

When the node moves, take both flags out

When the node moves, take out both flags, --discovery.dns and --bootnodes, and if you changed a config file, delete the EthDiscoveryURLs and SnapDiscoveryURLs lines you put there. The v1.13 line reads these lists and its own bootnodes by default: --bootnodes would hold it to the bootnodes it names, and --discovery.dns would leave it no lists for snap peers. Migrate Core-Geth has the steps, and its flags step says the same.

The Core-Geth v1.12 discovery endpoints

Why the v1.12 lists stop working

A v1.12 release starts from one DNS discovery list per network, published from etclabscore/discv4-dns-lists, and from the bootnodes compiled into it. From v1.12.20 to v1.12.23, those are four bootnodes with one hosting provider, three for Ethereum Classic and one for Mordor, and an attack in March 2026 repeatedly crashed two of the three Ethereum Classic ones. The ETC Cooperative's board has communicated that those lists will stop being updated as the Cooperative dissolves: they then serve an aging list of nodes until their domains lapse and they stop resolving. A v1.12 node with no saved peers it can still reach, such as a new node or one stopped long enough that its peers have moved on, then has nothing compiled in to start from.

ethereumclassic/discv4-dns-lists tells the history of the lists, says which nodes they carry, and shows how to check what DNS serves against what the repository recorded.