From 2beceb4547db420afc86efa706e43de48104c692 Mon Sep 17 00:00:00 2001 From: Yorick Downe Date: Wed, 2 Sep 2026 19:47:41 +0100 Subject: [PATCH] Nethermind 2.0 flat default support --- default.env | 6 +-- ethd | 72 +++++++++++++++++++++++++++++++-- nethermind.yml | 1 - nethermind/docker-entrypoint.sh | 54 +++++++++++-------------- 4 files changed, 93 insertions(+), 40 deletions(-) diff --git a/default.env b/default.env index 4b44c106..99fca986 100644 --- a/default.env +++ b/default.env @@ -48,6 +48,7 @@ CHECKPOINT_SYNC_URL=https://hoodi.checkpoint.sigp.io # detect duplicates of the validator(s) already running. Note this is NOT foolproof, though it can be useful when moving a node DOPPELGANGER=false # Auto-pruning Nethermind is usually safe. Set this to true to enable auto-pruning, and to anything else to disable it +# This parameter will be removed after Hegotá hardfork AUTOPRUNE_NM=true # Whether to use web3signer for validator keys. Setting this to "true" also requires web3signer.yml in COMPOSE_FILE WEB3SIGNER=false @@ -278,9 +279,6 @@ EL_NODE_TYPE=pre-merge-expiry # empty or "false" disables it, and any other string is assumed to be # exact parameters for Reth's "download" function RETH_SNAPSHOT= -# Nethermind FlatDB. Possible values empty (don't use FlatDB), flat, or flatintrie, see https://github.com/NethermindEth/nethermind/releases/tag/1.37.0 -# FlatDB cannot be used with an archive node. -NETHERMIND_FLATDB= # EraE URL, see https://github.com/eth-clients/e2store-format-specs/ and https://ethpandaops.io/data/history/ # If this URL is provided, an EL that supports EraE import will use it when fresh syncing # "urls.txt", "checksums.txt" and "checksums_sha256.txt" must exist at the target url @@ -584,4 +582,4 @@ DOCKER_SOCK=/var/run/docker.sock ALLOY_PROJECT_NAME= # Used by ethd update - please do not adjust -ENV_VERSION=68 +ENV_VERSION=69 diff --git a/ethd b/ethd index eaff9b35..10daf31e 100755 --- a/ethd +++ b/ethd @@ -1749,6 +1749,55 @@ __check_teku_db_version() { } +# Remove after Hegotá +__nethermind_is_patricia() { + local vol="$1" + + __dodocker volume inspect "${vol}" >/dev/null 2>&1 || return 1 + + __dodocker run --rm -v "${vol}:/var/lib/nethermind" alpine:3 sh -c ' + [ -d /var/lib/nethermind/nethermind_db ] || exit 1 + find /var/lib/nethermind/nethermind_db -mindepth 3 -maxdepth 3 \ + -path "*/flat/*" -name "*.sst" -print -quit | grep -q . && exit 1 + exit 0' +} + + +# Remove after Hegotá +__nethermind_has_db() { + local vol="$1" + + __dodocker volume inspect "${vol}" >/dev/null 2>&1 || return 1 + + __dodocker run --rm -v "${vol}:/var/lib/nethermind" alpine:3 [ -d /var/lib/nethermind/nethermind_db ] +} + + +# Remove after Hegotá +__check_nethermind_flat() { + local project_prefix + local volume_name + local og_volume_name + + if [[ ! "${COMPOSE_FILE}" =~ nethermind\.yml ]]; then + return 0 + fi + + project_prefix="$(basename "$(realpath .)" | tr '[:upper:]' '[:lower:]')" + volume_name="${project_prefix}_nethermind-el-data" + og_volume_name="${project_prefix}_nm-eth1-data" + + if __nethermind_has_db "${og_volume_name}"; then + __final_msg+="\nWARNING: Your Nethermind database is in the legacy \"nm-eth1-data\" Docker volume. ${__project_name} will stop supporting that volume with the Hegotá hard fork in 2027.\n" + __final_msg+="\nYou can move to the current volume by running \"${__me} resync-execution\".\n" + fi + if __nethermind_is_patricia "${volume_name}"; then + __final_msg+="\nWARNING: Your Nethermind database is using Patricia layout. This will continue to work but require manual state pruning setup with the Hegotá hard fork in 2027.\n" + __final_msg+="\nYou can switch to Flat layout by running \"${__me} resync-execution\".\n" + fi +} + + # Remove RIGHT after Glamsterdam __check_mev_boost() { local var @@ -2225,6 +2274,17 @@ __migrate_env() { __value+=" --LogIndex.Enabled true" fi fi + # Remove after Glamsterdam + if [[ "${__source_ver}" -lt "69" && "${var}" = "EL_EXTRAS" && "${COMPOSE_FILE}" =~ nethermind\.yml ]]; then + __get_value_from_env "NETHERMIND_FLATDB" "${__env_file}.source" "NETHERMIND_FLATDB" + if [[ "${NETHERMIND_FLATDB}" = "flatintrie" ]]; then + if [[ -z "${__value}" ]]; then + __value="--FlatDb.Layout=FlatInTrie" + elif [[ ! "${__value}" =~ --FlatDb\.Layout|flatdb-layout ]]; then + __value+=" --FlatDb.Layout=FlatInTrie" + fi + fi + fi if [[ "${var}" = "CL_QUIC_PORT" ]]; then __get_value_from_env "CL_P2P_PORT" "${__env_file}.source" "CL_P2P_PORT" @@ -2866,6 +2926,8 @@ update() { __check_teku_db_version # Remove RIGHT after Glamsterdam __check_mev_boost + # Remove after Hegotá + __check_nethermind_flat echo if [[ "${__env_migrated}" -eq 1 ]] && ! cmp -s "${__env_file}" "${__env_file}".source; then @@ -3398,6 +3460,7 @@ prune-reth() { prune-nethermind() { local exitstatus + local project_prefix local var local min_free local min_gib @@ -3444,10 +3507,11 @@ prune-nethermind() { fi # Check for FlatDB - var="NETHERMIND_FLATDB" - __get_value_from_env "${var}" "${__env_file}" "__value" - if [[ -n "${__value}" ]]; then - echo "Nethermind is configured for FlatDB, which need not and cannot be pruned. Aborting." + project_prefix="$(basename "$(realpath .)" | tr '[:upper:]' '[:lower:]')" + if ! __nethermind_is_patricia "${project_prefix}_nethermind-el-data" \ + && ! __nethermind_is_patricia "${project_prefix}_nm-eth1-data"; then + echo "Nethermind is not using Patricia layout - it is either on FlatDB, which need not and" + echo "cannot be pruned, or it has no database yet. Aborting." exit 1 fi diff --git a/nethermind.yml b/nethermind.yml index df9cfb2f..016e3738 100644 --- a/nethermind.yml +++ b/nethermind.yml @@ -29,7 +29,6 @@ services: - EL_EXTRAS=${EL_EXTRAS:-} - NODE_TYPE=${EL_NODE_TYPE:-pre-merge-expiry} - AUTOPRUNE_NM=${AUTOPRUNE_NM:-true} - - NM_FLATDB=${NETHERMIND_FLATDB:-} - NETWORK=${NETWORK} - ERE_URL=${ERE_URL:-} # For Grandine plugin diff --git a/nethermind/docker-entrypoint.sh b/nethermind/docker-entrypoint.sh index c612acf2..c77276cb 100755 --- a/nethermind/docker-entrypoint.sh +++ b/nethermind/docker-entrypoint.sh @@ -81,31 +81,20 @@ else __network="--config ${NETWORK}" fi -if [[ "${NODE_TYPE}" = "archive" ]]; then - __flat="" -else - case "${NM_FLATDB}" in - "") - __flat="" - ;; - flat) - echo "Enabling Nethermind FlatDB with Layout Flat" - #__flat="--FlatDb.Enabled=true --FlatDb.ImportFromPruningTrieState=true" - __flat="--FlatDb.Enabled=true" - ;; - flatintrie) - echo "Enabling Nethermind FlatDB with Layout FlatInTrie" - #__flat="--FlatDb.Enabled=true --FlatDb.ImportFromPruningTrieState=true --FlatDb.Layout=FlatInTrie" - __flat="--FlatDb.Enabled=true --FlatDb.Layout=FlatInTrie" - ;; - *) - __flat="" - echo "Unknown value ${NM_FLATDB} for \"NETHERMIND_FLATDB\". Continuing without FlatDB." - ;; - esac +# Determine the state DB layout. Flat is the Nethermind 2.0 default for a fresh DB; an existing +# DB keeps the layout it was created with. A fresh sync that opts out of flat counts as Patricia. +__patricia=0 +if [[ -d /var/lib/nethermind/nethermind_db || -d /var/lib/nethermind-og/nethermind_db ]]; then + if [[ -z "$(find /var/lib/nethermind/nethermind_db /var/lib/nethermind-og/nethermind_db \ + -mindepth 3 -maxdepth 3 -path '*/flat/*' -name '*.sst' -print -quit 2>/dev/null)" ]]; then + __patricia=1 + fi +elif [[ "${EL_EXTRAS,,}" =~ (flatdb\.enabled|flatdb-enabled)[[:space:]=]+false ]]; then + __patricia=1 fi -if [[ ! "${NETWORK}" =~ ^https?:// && "${NODE_TYPE}" != "archive" && -z "${__flat}" ]]; then # Only configure prune parameters for named networks, non-archive and HalfPath DB +# Only configure prune parameters for named networks, non-archive and Patricia DB +if [[ ! "${NETWORK}" =~ ^https?:// && "${NODE_TYPE}" != "archive" && "${__patricia}" -eq 1 ]]; then memtotal=$(awk '/MemTotal/ {printf "%d", int($2/1024/1024)}' /proc/meminfo) parallel=$(($(nproc)/4)) if [[ "${parallel}" -lt 2 ]]; then @@ -127,8 +116,16 @@ fi case "${NODE_TYPE}" in archive) - echo "Nethermind archive node without pruning" - __prune="--Sync.DownloadBodiesInFastSync=false --Sync.DownloadReceiptsInFastSync=false --Sync.FastSync=false --Sync.SnapSync=false --Sync.FastBlocks=false --Pruning.Mode=None --Sync.PivotNumber=0" + # Nethermind's own "_archive" config is this set plus the FlatDb keys below. We cannot + # use that config: its Init.BaseDbPath would move every existing archive user's datadir. + __prune="--Sync.DownloadBodiesInFastSync=false --Sync.DownloadReceiptsInFastSync=false --Sync.FastSync=false --Sync.SnapSync=false --Sync.FastBlocks=false --Pruning.Mode=None --Sync.PivotNumber=0 --Receipt.TxLookupLimit=0" + if [[ "${__patricia}" -eq 0 ]]; then + echo "Nethermind FlatDB archive node without pruning" + __prune+=" --FlatDb.HistoryEnabled=true --FlatDb.PersistenceWriteBufferFloor=67108864" + else + echo "Nethermind legacy archive node without pruning" + echo "Consider a FlatDB archive node instead" + fi __ere_from=0 ;; full) @@ -184,7 +181,6 @@ case "${NODE_TYPE}" in ;; custom) echo "Nethermind default block retention; adjust as desired by \"EL_EXTRAS\" in \".env\"" - echo "NB: \"AUTOPRUNE_NM\" has no effect; if auto-pruning is desired, add the parameters yourself" __prune="" __ere_from=0 ;; @@ -225,10 +221,6 @@ if [[ -n "${__ere}" ]]; then echo "Using EraE import parameters:" echo "${__ere}" fi -if [[ -n "${__flat}" ]]; then - echo "Using FlatDB parameters:" - echo "${__flat}" -fi if [[ "${COMPOSE_FILE}" =~ grandine-plugin(-allin1)?\.yml ]]; then if [[ ! -f /var/lib/grandine/wallet-password.txt ]]; then @@ -386,4 +378,4 @@ set -- "${__args[@]}" # Word splitting is desired for the command line parameters # shellcheck disable=SC2086 -exec "$@" ${__datadir} ${__network} ${__prune} ${__ere} ${__flat} ${__grandine} "${__grandine_graffiti_args[@]}" ${EL_EXTRAS} +exec "$@" ${__datadir} ${__network} ${__prune} ${__ere} ${__grandine} "${__grandine_graffiti_args[@]}" ${EL_EXTRAS}