From 576a9a668a3041d2707dbf63a56d2b5c809f11c9 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Mon, 4 Sep 2023 21:57:35 +0200 Subject: [PATCH] add infrastructure to select fork choice version To allow testing https://github.com/ethereum/consensus-specs/issues/3466 add support for selecting fork choice version at launch. This means we can deploy a different logic when `DENEB_FORK_EPOCH != FAR_FUTURE_EPOCH` that won't be used on Mainnet. --- beacon_chain/conf.nim | 8 ++++++++ .../consensus_object_pools/attestation_pool.nim | 3 ++- beacon_chain/fork_choice/fork_choice.nim | 12 ++++++++---- beacon_chain/fork_choice/fork_choice_types.nim | 7 +++++++ beacon_chain/fork_choice/proto_array.nim | 8 +++++--- beacon_chain/nimbus_beacon_node.nim | 6 ++++-- beacon_chain/rpc/rest_debug_api.nim | 4 +++- beacon_chain/spec/eth2_apis/rest_types.nim | 2 +- tests/consensus_spec/test_fixture_fork_choice.nim | 3 ++- tests/test_keymanager_api.nim | 3 ++- 10 files changed, 42 insertions(+), 14 deletions(-) diff --git a/beacon_chain/conf.nim b/beacon_chain/conf.nim index 4f8a32ef99..bb81d488f9 100644 --- a/beacon_chain/conf.nim +++ b/beacon_chain/conf.nim @@ -29,6 +29,8 @@ import ./el/el_conf, ./filepath +from fork_choice/fork_choice_types + import ForkChoiceVersion from consensus_object_pools/block_pools_types_light_client import LightClientDataImportMode @@ -632,6 +634,12 @@ type desc: "Bandwidth estimate for the node (bits per second)" name: "debug-bandwidth-estimate" .}: Option[Natural] + forkChoiceVersion* {. + hidden + desc: "Forkchoice version to use. " & + "Must be one of: stable" + name: "debug-forkchoice-version" .}: Option[ForkChoiceVersion] + of BNStartUpCmd.wallets: case walletsCmd* {.command.}: WalletsCmd of WalletsCmd.create: diff --git a/beacon_chain/consensus_object_pools/attestation_pool.nim b/beacon_chain/consensus_object_pools/attestation_pool.nim index efa4fc40df..477c05205d 100644 --- a/beacon_chain/consensus_object_pools/attestation_pool.nim +++ b/beacon_chain/consensus_object_pools/attestation_pool.nim @@ -90,6 +90,7 @@ declareGauge attestation_pool_block_attestation_packing_time, proc init*(T: type AttestationPool, dag: ChainDAGRef, quarantine: ref Quarantine, + forkChoiceVersion = ForkChoiceVersion.Stable, onAttestation: OnAttestationCallback = nil): T = ## Initialize an AttestationPool from the dag `headState` ## The `finalized_root` works around the finalized_checkpoint of the genesis block @@ -97,7 +98,7 @@ proc init*(T: type AttestationPool, dag: ChainDAGRef, let finalizedEpochRef = dag.getFinalizedEpochRef() var forkChoice = ForkChoice.init( - finalizedEpochRef, dag.finalizedHead.blck) + finalizedEpochRef, dag.finalizedHead.blck, forkChoiceVersion) # Feed fork choice with unfinalized history - during startup, block pool only # keeps track of a single history so we just need to follow it diff --git a/beacon_chain/fork_choice/fork_choice.nim b/beacon_chain/fork_choice/fork_choice.nim index 2311f0f395..710789e4c5 100644 --- a/beacon_chain/fork_choice/fork_choice.nim +++ b/beacon_chain/fork_choice/fork_choice.nim @@ -49,11 +49,13 @@ func compute_deltas( logScope: topics = "fork_choice" func init*( - T: type ForkChoiceBackend, checkpoints: FinalityCheckpoints): T = - T(proto_array: ProtoArray.init(checkpoints)) + T: type ForkChoiceBackend, checkpoints: FinalityCheckpoints, + version: ForkChoiceVersion): T = + T(proto_array: ProtoArray.init(checkpoints, version)) proc init*( - T: type ForkChoice, epochRef: EpochRef, blck: BlockRef): T = + T: type ForkChoice, epochRef: EpochRef, blck: BlockRef, + version: ForkChoiceVersion): T = ## Initialize a fork choice context for a finalized state - in the finalized ## state, the justified and finalized checkpoints are the same, so only one ## is used here @@ -65,8 +67,10 @@ proc init*( backend: ForkChoiceBackend.init( FinalityCheckpoints( justified: checkpoint, - finalized: checkpoint)), + finalized: checkpoint), + version), checkpoints: Checkpoints( + version: version, justified: BalanceCheckpoint( checkpoint: checkpoint, balances: epochRef.effective_balances), diff --git a/beacon_chain/fork_choice/fork_choice_types.nim b/beacon_chain/fork_choice/fork_choice_types.nim index 5931672597..27a94980b9 100644 --- a/beacon_chain/fork_choice/fork_choice_types.nim +++ b/beacon_chain/fork_choice/fork_choice_types.nim @@ -29,6 +29,11 @@ import # ---------------------------------------------------------------------- type + ForkChoiceVersion* {.pure.} = enum + ## Controls which version of fork choice to run. + Stable = "stable" + ## Use current version from stable Ethereum consensus specifications + fcKind* = enum ## Fork Choice Error Kinds fcFinalizedNodeUnknown @@ -88,6 +93,7 @@ type ## Subtracted from logical index to get the physical index ProtoArray* = object + version*: ForkChoiceVersion currentEpoch*: Epoch checkpoints*: FinalityCheckpoints nodes*: ProtoNodes @@ -110,6 +116,7 @@ type balances*: seq[Gwei] Checkpoints* = object + version*: ForkChoiceVersion time*: BeaconTime justified*: BalanceCheckpoint finalized*: Checkpoint diff --git a/beacon_chain/fork_choice/proto_array.nim b/beacon_chain/fork_choice/proto_array.nim index 558b985d1c..b0d125cdca 100644 --- a/beacon_chain/fork_choice/proto_array.nim +++ b/beacon_chain/fork_choice/proto_array.nim @@ -90,7 +90,8 @@ func nodeLeadsToViableHead( # ---------------------------------------------------------------------- func init*( - T: type ProtoArray, checkpoints: FinalityCheckpoints): T = + T: type ProtoArray, checkpoints: FinalityCheckpoints, + version: ForkChoiceVersion): T = let node = ProtoNode( bid: BlockId( slot: checkpoints.finalized.epoch.start_slot, @@ -102,7 +103,8 @@ func init*( bestChild: none(int), bestDescendant: none(int)) - T(checkpoints: checkpoints, + T(version: version, + checkpoints: checkpoints, nodes: ProtoNodes(buf: @[node], offset: 0), indices: {node.bid.root: 0}.toTable()) @@ -541,7 +543,7 @@ func nodeIsViableForHead( correctJustified = unrealized.justified.epoch >= self.checkpoints.justified.epoch and node.checkpoints.justified.epoch + 2 >= self.currentEpoch - + return if not correctJustified: false diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 0c160ded91..e85b8df201 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -313,8 +313,8 @@ proc initFullNode( let quarantine = newClone( Quarantine.init()) - attestationPool = newClone( - AttestationPool.init(dag, quarantine, onAttestationReceived)) + attestationPool = newClone(AttestationPool.init( + dag, quarantine, config.forkChoiceVersion.get, onAttestationReceived)) syncCommitteeMsgPool = newClone( SyncCommitteeMsgPool.init(rng, dag.cfg, onSyncContribution)) lightClientPool = newClone( @@ -1860,6 +1860,8 @@ proc doRunBeaconNode(config: var BeaconNodeConf, rng: ref HmacDrbgContext) {.rai # works for node in metadata.bootstrapNodes: config.bootstrapNodes.add node + if config.forkChoiceVersion.isNone: + config.forkChoiceVersion = some(ForkChoiceVersion.Stable) ## Ctrl+C handling proc controlCHandler() {.noconv.} = diff --git a/beacon_chain/rpc/rest_debug_api.nim b/beacon_chain/rpc/rest_debug_api.nim index 2a6f65a4ed..fe52f27256 100644 --- a/beacon_chain/rpc/rest_debug_api.nim +++ b/beacon_chain/rpc/rest_debug_api.nim @@ -87,7 +87,9 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) = var response = GetForkChoiceResponse( justified_checkpoint: forkChoice.checkpoints.justified.checkpoint, - finalized_checkpoint: forkChoice.checkpoints.finalized) + finalized_checkpoint: forkChoice.checkpoints.finalized, + extra_data: RestExtraData( + version: some($forkChoice.backend.proto_array.version))) for item in forkChoice.backend.proto_array: let diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index df75b05276..2aac353803 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -712,7 +712,7 @@ type extra_data*: Option[RestNodeExtraData] RestExtraData* = object - discard + version*: Option[string] GetForkChoiceResponse* = object justified_checkpoint*: Checkpoint diff --git a/tests/consensus_spec/test_fixture_fork_choice.nim b/tests/consensus_spec/test_fixture_fork_choice.nim index 5035c747c7..a3bd37879e 100644 --- a/tests/consensus_spec/test_fixture_fork_choice.nim +++ b/tests/consensus_spec/test_fixture_fork_choice.nim @@ -123,7 +123,8 @@ proc initialLoad( dag = ChainDAGRef.init( forkedState[].kind.genesisTestRuntimeConfig, db, validatorMonitor, {}) fkChoice = newClone(ForkChoice.init( - dag.getFinalizedEpochRef(), dag.finalizedHead.blck)) + dag.getFinalizedEpochRef(), dag.finalizedHead.blck, + ForkChoiceVersion.Stable)) (dag, fkChoice) diff --git a/tests/test_keymanager_api.nim b/tests/test_keymanager_api.nim index b1a6a70679..3c0ed672e2 100644 --- a/tests/test_keymanager_api.nim +++ b/tests/test_keymanager_api.nim @@ -295,7 +295,8 @@ proc startBeaconNode(basePort: int) {.raises: [CatchableError].} = "--keymanager-port=" & $(basePort + PortKind.KeymanagerBN.ord), "--keymanager-token-file=" & tokenFilePath, "--suggested-fee-recipient=" & $defaultFeeRecipient, - "--doppelganger-detection=off"], it)) + "--doppelganger-detection=off", + "--debug-forkchoice-version=stable"], it)) except Exception as exc: # TODO fix confutils exceptions raiseAssert exc.msg