From 6c3bbbf22cabd389bf667702c31307f533057abb Mon Sep 17 00:00:00 2001 From: andri lim Date: Wed, 6 Nov 2024 09:01:42 +0700 Subject: [PATCH] Feature: Prevent loading an existing data directory for the wrong network (#2825) * Prevent loading an existing data directory for the wrong network * Fix and add more info --- nimbus/common/chain_config_hash.nim | 84 +++++++++++++++++++++++++++++ nimbus/db/storage_types.nim | 6 ++- nimbus/nimbus_execution_client.nim | 24 ++++++++- 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 nimbus/common/chain_config_hash.nim diff --git a/nimbus/common/chain_config_hash.nim b/nimbus/common/chain_config_hash.nim new file mode 100644 index 000000000..9bf2e1661 --- /dev/null +++ b/nimbus/common/chain_config_hash.nim @@ -0,0 +1,84 @@ +# Nimbus +# Copyright (c) 2021-2024 Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) +# at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +{.push raises: [].} + +import + std/[typetraits, tables], + eth/common/base, + eth/common/times, + eth/common/hashes, + eth/common/addresses, + stew/endians2, + stint, + nimcrypto/sha2, + ./chain_config + +# ------------------------------------------------------------------------------ +# When the client doing initialization step, it will go through +# complicated steps before the genesis hash is ready. See `CommonRef.init`. +# If the genesis happen to exists in database belonging to other network, +# it will replace the one in CommonRef cache. +# That is the reason why using genesis header or genesis hash + ChainId is +# not a good solution to prevent loading existing data directory for +# the wrong network. +# But the ChainConfig + raw Genesis hash will make the job done before +# CommonRef creation. +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# Private helper functions +# ------------------------------------------------------------------------------ + +func update(ctx: var sha256, val: uint64 | UInt256) = + ctx.update(val.toBytesLE) + +func update(ctx: var sha256, val: ChainId | EthTime | NetworkId) = + ctx.update(distinctBase val) + +func update(ctx: var sha256, val: bool) = + ctx.update([val.byte]) + +func update(ctx: var sha256, val: Hash32 | Bytes8 | Bytes32 | Address) = + ctx.update(val.data) + +func update[T](ctx: var sha256, val: Opt[T]) = + if val.isSome: + ctx.update(val.get) + +func update[K, V](ctx: var sha256, val: Table[K, V]) = + mixin update + for k, v in val: + ctx.update(k) + ctx.update(v) + +func update[T: object](ctx: var sha256, val: T) = + for f in fields(val): + ctx.update(f) + +func update[T: ref](ctx: var sha256, val: T) = + for f in fields(val[]): + ctx.update(f) + +# ------------------------------------------------------------------------------ +# Public functions +# ------------------------------------------------------------------------------ + +func calcHash*(networkId: NetworkId, conf: ChainConfig, genesis: Genesis): Hash32 = + var ctx: sha256 + ctx.init() + ctx.update(networkId) + ctx.update(conf) + if genesis.isNil.not: + ctx.update(genesis) + ctx.finish(result.data) + ctx.clear() + +func calcHash*(networkId: NetworkId, params: NetworkParams): Hash32 = + calcHash(networkId, params.config, params.genesis) diff --git a/nimbus/db/storage_types.nim b/nimbus/db/storage_types.nim index e8e29ab0e..392048c67 100644 --- a/nimbus/db/storage_types.nim +++ b/nimbus/db/storage_types.nim @@ -21,7 +21,7 @@ type canonicalHeadHash = 4 slotHashToSlot = 5 contractHash = 6 - transitionStatus = 7 + dataDirId = 7 safeHash = 8 finalizedHash = 9 beaconState = 10 @@ -59,6 +59,10 @@ func canonicalHeadHashKey*(): DbKey {.inline.} = result.data[0] = byte ord(canonicalHeadHash) result.dataEndPos = 1 +func dataDirIdKey*(): DbKey {.inline.} = + result.data[0] = byte ord(dataDirId) + result.dataEndPos = 1 + func slotHashToSlotKey*(h: openArray[byte]): DbKey {.inline.} = doAssert(h.len == 32) result.data[0] = byte ord(slotHashToSlot) diff --git a/nimbus/nimbus_execution_client.nim b/nimbus/nimbus_execution_client.nim index 892eca268..342b9fbc5 100644 --- a/nimbus/nimbus_execution_client.nim +++ b/nimbus/nimbus_execution_client.nim @@ -17,6 +17,7 @@ import metrics, metrics/chronicles_support, kzg4844/kzg, + stew/byteutils, ./rpc, ./version, ./constants, @@ -24,7 +25,9 @@ import ./nimbus_import, ./core/eip4844, ./db/core_db/persistent, - ./sync/handlers + ./db/storage_types, + ./sync/handlers, + ./common/chain_config_hash from beacon_chain/nimbus_binary_common import setupFileLimits @@ -153,6 +156,24 @@ proc setupMetrics(nimbus: NimbusNode, conf: NimbusConf) = nimbus.metricsServer = res.get waitFor nimbus.metricsServer.start() +proc preventLoadingDataDirForTheWrongNetwork(db: CoreDbRef; conf: NimbusConf) = + let + kvt = db.ctx.getKvt() + calculatedId = calcHash(conf.networkId, conf.networkParams) + dataDirIdBytes = kvt.get(dataDirIdKey().toOpenArray).valueOr: + # an empty database + info "Writing data dir ID", ID=calculatedId + kvt.put(dataDirIdKey().toOpenArray, calculatedId.data).isOkOr: + fatal "Cannot write data dir ID", ID=calculatedId + quit(QuitFailure) + return + + if calculatedId.data != dataDirIdBytes: + fatal "Data dir already initialized with other network configuration", + get=dataDirIdBytes.toHex, + expected=calculatedId + quit(QuitFailure) + proc run(nimbus: NimbusNode, conf: NimbusConf) = ## logging setLogLevel(conf.logLevel) @@ -192,6 +213,7 @@ proc run(nimbus: NimbusNode, conf: NimbusConf) = string conf.dataDir, conf.dbOptions(noKeyCache = conf.cmd == NimbusCmd.`import`)) + preventLoadingDataDirForTheWrongNetwork(coreDB, conf) setupMetrics(nimbus, conf) let com = CommonRef.new(