From a191296f05647bb5597bf618a6707cd1f1738164 Mon Sep 17 00:00:00 2001
From: Bayram Guvanjov <bayramguwanjow@gmail.com>
Date: Fri, 12 Jan 2024 22:20:26 +0900
Subject: [PATCH] Deneb integration to Caplin (#9093)

Pr is ready to review and merge.

This PR includes implementing and integrating Ethereum Deneb's hard work
with the Caplin Ethereum client.

Changes:

- Full compatibility with Deneb Ethereum hard fork
- Added new EIPs introduced in Deneb. (`EIP-4788`, `EIP-4844`,
`EIP-7044`, `EIP-7045`, `EIP-7514`)
- Tests integration

---------

Co-authored-by: Giulio <giulio.rebuffo@gmail.com>
---
 cl/abstract/beacon_state.go                   |  1 +
 cl/clparams/config.go                         | 54 ++++++++++---------
 cl/cltypes/eth1_header.go                     |  2 +-
 cl/phase1/core/state/cache_accessors.go       | 13 ++++-
 cl/phase1/forkchoice/on_operations.go         | 11 +++-
 cl/spectest/Makefile                          |  7 ++-
 cl/transition/impl/eth2/operations.go         | 12 ++++-
 .../statechange/process_registry_updates.go   |  2 +-
 8 files changed, 66 insertions(+), 36 deletions(-)

diff --git a/cl/abstract/beacon_state.go b/cl/abstract/beacon_state.go
index 304a414dd..cc77a2061 100644
--- a/cl/abstract/beacon_state.go
+++ b/cl/abstract/beacon_state.go
@@ -40,6 +40,7 @@ type BeaconStateExtension interface {
 	ValidatorIndexByPubkey(key [48]byte) (uint64, bool)
 	PreviousStateRoot() common.Hash
 	SetPreviousStateRoot(root common.Hash)
+	GetValidatorActivationChurnLimit() uint64
 }
 
 type BeaconStateBasic interface {
diff --git a/cl/clparams/config.go b/cl/clparams/config.go
index 4d51e918d..8748f7a8f 100644
--- a/cl/clparams/config.go
+++ b/cl/clparams/config.go
@@ -308,20 +308,21 @@ type BeaconChainConfig struct {
 	JustificationBitsLength  uint64 `yaml:"JUSTIFICATION_BITS_LENGTH"`   // JustificationBitsLength defines number of epochs to track when implementing k-finality in Casper FFG.
 
 	// Misc constants.
-	PresetBase                     string `yaml:"PRESET_BASE" spec:"true"`                        // PresetBase represents the underlying spec preset this config is based on.
-	ConfigName                     string `yaml:"CONFIG_NAME" spec:"true"`                        // ConfigName for allowing an easy human-readable way of knowing what chain is being used.
-	TargetCommitteeSize            uint64 `yaml:"TARGET_COMMITTEE_SIZE" spec:"true"`              // TargetCommitteeSize is the number of validators in a committee when the chain is healthy.
-	MaxValidatorsPerCommittee      uint64 `yaml:"MAX_VALIDATORS_PER_COMMITTEE" spec:"true"`       // MaxValidatorsPerCommittee defines the upper bound of the size of a committee.
-	MaxCommitteesPerSlot           uint64 `yaml:"MAX_COMMITTEES_PER_SLOT" spec:"true"`            // MaxCommitteesPerSlot defines the max amount of committee in a single slot.
-	MinPerEpochChurnLimit          uint64 `yaml:"MIN_PER_EPOCH_CHURN_LIMIT" spec:"true"`          // MinPerEpochChurnLimit is the minimum amount of churn allotted for validator rotations.
-	ChurnLimitQuotient             uint64 `yaml:"CHURN_LIMIT_QUOTIENT" spec:"true"`               // ChurnLimitQuotient is used to determine the limit of how many validators can rotate per epoch.
-	ShuffleRoundCount              uint64 `yaml:"SHUFFLE_ROUND_COUNT" spec:"true"`                // ShuffleRoundCount is used for retrieving the permuted index.
-	MinGenesisActiveValidatorCount uint64 `yaml:"MIN_GENESIS_ACTIVE_VALIDATOR_COUNT" spec:"true"` // MinGenesisActiveValidatorCount defines how many validator deposits needed to kick off beacon chain.
-	MinGenesisTime                 uint64 `yaml:"MIN_GENESIS_TIME" spec:"true"`                   // MinGenesisTime is the time that needed to pass before kicking off beacon chain.
-	TargetAggregatorsPerCommittee  uint64 `yaml:"TARGET_AGGREGATORS_PER_COMMITTEE" spec:"true"`   // TargetAggregatorsPerCommittee defines the number of aggregators inside one committee.
-	HysteresisQuotient             uint64 `yaml:"HYSTERESIS_QUOTIENT" spec:"true"`                // HysteresisQuotient defines the hysteresis quotient for effective balance calculations.
-	HysteresisDownwardMultiplier   uint64 `yaml:"HYSTERESIS_DOWNWARD_MULTIPLIER" spec:"true"`     // HysteresisDownwardMultiplier defines the hysteresis downward multiplier for effective balance calculations.
-	HysteresisUpwardMultiplier     uint64 `yaml:"HYSTERESIS_UPWARD_MULTIPLIER" spec:"true"`       // HysteresisUpwardMultiplier defines the hysteresis upward multiplier for effective balance calculations.
+	PresetBase                      string `yaml:"PRESET_BASE" spec:"true"`                          // PresetBase represents the underlying spec preset this config is based on.
+	ConfigName                      string `yaml:"CONFIG_NAME" spec:"true"`                          // ConfigName for allowing an easy human-readable way of knowing what chain is being used.
+	TargetCommitteeSize             uint64 `yaml:"TARGET_COMMITTEE_SIZE" spec:"true"`                // TargetCommitteeSize is the number of validators in a committee when the chain is healthy.
+	MaxValidatorsPerCommittee       uint64 `yaml:"MAX_VALIDATORS_PER_COMMITTEE" spec:"true"`         // MaxValidatorsPerCommittee defines the upper bound of the size of a committee.
+	MaxCommitteesPerSlot            uint64 `yaml:"MAX_COMMITTEES_PER_SLOT" spec:"true"`              // MaxCommitteesPerSlot defines the max amount of committee in a single slot.
+	MinPerEpochChurnLimit           uint64 `yaml:"MIN_PER_EPOCH_CHURN_LIMIT" spec:"true"`            // MinPerEpochChurnLimit is the minimum amount of churn allotted for validator rotations.
+	ChurnLimitQuotient              uint64 `yaml:"CHURN_LIMIT_QUOTIENT" spec:"true"`                 // ChurnLimitQuotient is used to determine the limit of how many validators can rotate per epoch.
+	MaxPerEpochActivationChurnLimit uint64 `yaml:"MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT" spec:"true"` // MaxPerEpochActivationChurnLimit defines the maximum amount of churn allowed in one epoch from deneb.
+	ShuffleRoundCount               uint64 `yaml:"SHUFFLE_ROUND_COUNT" spec:"true"`                  // ShuffleRoundCount is used for retrieving the permuted index.
+	MinGenesisActiveValidatorCount  uint64 `yaml:"MIN_GENESIS_ACTIVE_VALIDATOR_COUNT" spec:"true"`   // MinGenesisActiveValidatorCount defines how many validator deposits needed to kick off beacon chain.
+	MinGenesisTime                  uint64 `yaml:"MIN_GENESIS_TIME" spec:"true"`                     // MinGenesisTime is the time that needed to pass before kicking off beacon chain.
+	TargetAggregatorsPerCommittee   uint64 `yaml:"TARGET_AGGREGATORS_PER_COMMITTEE" spec:"true"`     // TargetAggregatorsPerCommittee defines the number of aggregators inside one committee.
+	HysteresisQuotient              uint64 `yaml:"HYSTERESIS_QUOTIENT" spec:"true"`                  // HysteresisQuotient defines the hysteresis quotient for effective balance calculations.
+	HysteresisDownwardMultiplier    uint64 `yaml:"HYSTERESIS_DOWNWARD_MULTIPLIER" spec:"true"`       // HysteresisDownwardMultiplier defines the hysteresis downward multiplier for effective balance calculations.
+	HysteresisUpwardMultiplier      uint64 `yaml:"HYSTERESIS_UPWARD_MULTIPLIER" spec:"true"`         // HysteresisUpwardMultiplier defines the hysteresis upward multiplier for effective balance calculations.
 
 	// Gwei value constants.
 	MinDepositAmount          uint64 `yaml:"MIN_DEPOSIT_AMOUNT" spec:"true"`          // MinDepositAmount is the minimum amount of Gwei a validator can send to the deposit contract at once (lower amounts will be reverted).
@@ -573,18 +574,19 @@ var MainnetBeaconConfig BeaconChainConfig = BeaconChainConfig{
 	GenesisDelay:             604800, // 1 week.
 
 	// Misc constant.
-	TargetCommitteeSize:            128,
-	MaxValidatorsPerCommittee:      2048,
-	MaxCommitteesPerSlot:           64,
-	MinPerEpochChurnLimit:          4,
-	ChurnLimitQuotient:             1 << 16,
-	ShuffleRoundCount:              90,
-	MinGenesisActiveValidatorCount: 16384,
-	MinGenesisTime:                 1606824000, // Dec 1, 2020, 12pm UTC.
-	TargetAggregatorsPerCommittee:  16,
-	HysteresisQuotient:             4,
-	HysteresisDownwardMultiplier:   1,
-	HysteresisUpwardMultiplier:     5,
+	TargetCommitteeSize:             128,
+	MaxValidatorsPerCommittee:       2048,
+	MaxCommitteesPerSlot:            64,
+	MinPerEpochChurnLimit:           4,
+	ChurnLimitQuotient:              1 << 16,
+	MaxPerEpochActivationChurnLimit: 8,
+	ShuffleRoundCount:               90,
+	MinGenesisActiveValidatorCount:  16384,
+	MinGenesisTime:                  1606824000, // Dec 1, 2020, 12pm UTC.
+	TargetAggregatorsPerCommittee:   16,
+	HysteresisQuotient:              4,
+	HysteresisDownwardMultiplier:    1,
+	HysteresisUpwardMultiplier:      5,
 
 	// Gwei value constants.
 	MinDepositAmount:          1 * 1e9,
diff --git a/cl/cltypes/eth1_header.go b/cl/cltypes/eth1_header.go
index 9eabdfc1a..e74270fc6 100644
--- a/cl/cltypes/eth1_header.go
+++ b/cl/cltypes/eth1_header.go
@@ -57,7 +57,7 @@ func (e *Eth1Header) Capella() {
 	e.WithdrawalsRoot = libcommon.Hash{}
 }
 
-// Capella converts the header to capella version.
+// Deneb converts the header to deneb version.
 func (e *Eth1Header) Deneb() {
 	e.version = clparams.DenebVersion
 	e.BlobGasUsed = 0
diff --git a/cl/phase1/core/state/cache_accessors.go b/cl/phase1/core/state/cache_accessors.go
index 99f2f17a9..80685ec5a 100644
--- a/cl/phase1/core/state/cache_accessors.go
+++ b/cl/phase1/core/state/cache_accessors.go
@@ -171,7 +171,10 @@ func (b *CachingBeaconState) GetAttestationParticipationFlagIndicies(data solid.
 	if inclusionDelay <= utils.IntegerSquareRoot(b.BeaconConfig().SlotsPerEpoch) {
 		participationFlagIndicies = append(participationFlagIndicies, b.BeaconConfig().TimelySourceFlagIndex)
 	}
-	if matchingTarget && inclusionDelay <= b.BeaconConfig().SlotsPerEpoch {
+	if b.Version() < clparams.DenebVersion && matchingTarget && inclusionDelay <= b.BeaconConfig().SlotsPerEpoch {
+		participationFlagIndicies = append(participationFlagIndicies, b.BeaconConfig().TimelyTargetFlagIndex)
+	}
+	if b.Version() >= clparams.DenebVersion && matchingTarget {
 		participationFlagIndicies = append(participationFlagIndicies, b.BeaconConfig().TimelyTargetFlagIndex)
 	}
 	if matchingHead && inclusionDelay == b.BeaconConfig().MinAttestationInclusionDelay {
@@ -295,3 +298,11 @@ func (b *CachingBeaconState) GetValidatorChurnLimit() uint64 {
 	activeIndsCount := uint64(len(b.GetActiveValidatorsIndices(Epoch(b))))
 	return utils.Max64(activeIndsCount/b.BeaconConfig().ChurnLimitQuotient, b.BeaconConfig().MinPerEpochChurnLimit)
 }
+
+// https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#new-get_validator_activation_churn_limit
+func (b *CachingBeaconState) GetValidatorActivationChurnLimit() uint64 {
+	if b.Version() >= clparams.DenebVersion {
+		return utils.Min64(b.BeaconConfig().MaxPerEpochActivationChurnLimit, b.GetValidatorChurnLimit())
+	}
+	return b.GetValidatorChurnLimit()
+}
diff --git a/cl/phase1/forkchoice/on_operations.go b/cl/phase1/forkchoice/on_operations.go
index 8679fb6a9..743995231 100644
--- a/cl/phase1/forkchoice/on_operations.go
+++ b/cl/phase1/forkchoice/on_operations.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 
 	"github.com/Giulio2002/bls"
+	"github.com/ledgerwatch/erigon/cl/clparams"
 	"github.com/ledgerwatch/erigon/cl/cltypes"
 	"github.com/ledgerwatch/erigon/cl/fork"
 	"github.com/ledgerwatch/erigon/cl/phase1/core/state"
@@ -49,10 +50,18 @@ func (f *ForkChoiceStore) OnVoluntaryExit(signedVoluntaryExit *cltypes.SignedVol
 	pk := val.PublicKey()
 	f.mu.Unlock()
 
-	domain, err := s.GetDomain(s.BeaconConfig().DomainVoluntaryExit, voluntaryExit.Epoch)
+	domainType := f.beaconCfg.DomainVoluntaryExit
+	var domain []byte
+
+	if s.Version() < clparams.DenebVersion {
+		domain, err = s.GetDomain(domainType, voluntaryExit.Epoch)
+	} else if s.Version() >= clparams.DenebVersion {
+		domain, err = fork.ComputeDomain(domainType[:], utils.Uint32ToBytes4(s.BeaconConfig().CapellaForkVersion), s.GenesisValidatorsRoot())
+	}
 	if err != nil {
 		return err
 	}
+
 	signingRoot, err := fork.ComputeSigningRoot(voluntaryExit, domain)
 	if err != nil {
 		return err
diff --git a/cl/spectest/Makefile b/cl/spectest/Makefile
index 42877b2a3..23c6cf4c1 100644
--- a/cl/spectest/Makefile
+++ b/cl/spectest/Makefile
@@ -3,15 +3,14 @@
 
 tests:
 	GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/ethereum/consensus-spec-tests
-	cd consensus-spec-tests && git checkout 99549a414c10baa9e69abcb08eb256fc1a8d54f6 && git lfs pull --exclude=tests/general,tests/minimal && cd ..
+	cd consensus-spec-tests && git checkout 080c96fbbf3be58e75947debfeb9ba3b2b7c9748 && git lfs pull --exclude=tests/general,tests/minimal && cd ..
 	mv consensus-spec-tests/tests .
 	rm -rf consensus-spec-tests
 	rm -rf tests/minimal
 	# not needed for now
 	rm -rf tests/mainnet/eip6110
-	# will not implement until i see it on a testnet
-	rm -rf tests/mainnet/deneb
-
+	# FIXME: Add fork choice coverage for deneb
+	rm -rf tests/mainnet/deneb/fork_choice
 clean:
 	rm -rf tests
 
diff --git a/cl/transition/impl/eth2/operations.go b/cl/transition/impl/eth2/operations.go
index 0650a1c1c..fd570038f 100644
--- a/cl/transition/impl/eth2/operations.go
+++ b/cl/transition/impl/eth2/operations.go
@@ -217,7 +217,12 @@ func (I *impl) ProcessVoluntaryExit(s abstract.BeaconState, signedVoluntaryExit
 
 	// We can skip it in some instances if we want to optimistically sync up.
 	if I.FullValidation {
-		domain, err := s.GetDomain(s.BeaconConfig().DomainVoluntaryExit, voluntaryExit.Epoch)
+		var domain []byte
+		if s.Version() < clparams.DenebVersion {
+			domain, err = s.GetDomain(s.BeaconConfig().DomainVoluntaryExit, voluntaryExit.Epoch)
+		} else if s.Version() >= clparams.DenebVersion {
+			domain, err = fork.ComputeDomain(s.BeaconConfig().DomainVoluntaryExit[:], utils.Uint32ToBytes4(s.BeaconConfig().CapellaForkVersion), s.GenesisValidatorsRoot())
+		}
 		if err != nil {
 			return err
 		}
@@ -697,7 +702,10 @@ func (I *impl) processAttestation(s abstract.BeaconState, attestation *solid.Att
 	if (data.Target().Epoch() != currentEpoch && data.Target().Epoch() != previousEpoch) || data.Target().Epoch() != state.GetEpochAtSlot(s.BeaconConfig(), data.Slot()) {
 		return nil, errors.New("ProcessAttestation: attestation with invalid epoch")
 	}
-	if data.Slot()+beaconConfig.MinAttestationInclusionDelay > stateSlot || stateSlot > data.Slot()+beaconConfig.SlotsPerEpoch {
+	if s.Version() < clparams.DenebVersion && ((data.Slot()+beaconConfig.MinAttestationInclusionDelay > stateSlot) || (stateSlot > data.Slot()+beaconConfig.SlotsPerEpoch)) {
+		return nil, errors.New("ProcessAttestation: attestation slot not in range")
+	}
+	if s.Version() >= clparams.DenebVersion && data.Slot()+beaconConfig.MinAttestationInclusionDelay > stateSlot {
 		return nil, errors.New("ProcessAttestation: attestation slot not in range")
 	}
 	if data.ValidatorIndex() >= s.CommitteeCount(data.Target().Epoch()) {
diff --git a/cl/transition/impl/eth2/statechange/process_registry_updates.go b/cl/transition/impl/eth2/statechange/process_registry_updates.go
index c22c44226..ee56a2f0e 100644
--- a/cl/transition/impl/eth2/statechange/process_registry_updates.go
+++ b/cl/transition/impl/eth2/statechange/process_registry_updates.go
@@ -62,7 +62,7 @@ func ProcessRegistryUpdates(s abstract.BeaconState) error {
 		}
 		return activationQueue[i].validatorIndex < activationQueue[j].validatorIndex
 	})
-	activationQueueLength := s.GetValidatorChurnLimit()
+	activationQueueLength := s.GetValidatorActivationChurnLimit()
 	if len(activationQueue) > int(activationQueueLength) {
 		activationQueue = activationQueue[:activationQueueLength]
 	}