erigon-pulse/cl/persistence/state/static_validator_table.go
Giulio rebuffo c477281362
Caplin: Parallel historical states reconstruction (#8817)
What does this PR do:
* Optional Backfilling and Caplin Archive Node
* Create antiquary for historical states
* Fixed gaps of chain gap related to the Head of the chain and anchor of
the chain.
* Added basic reader object to Read the Historical state
2023-12-06 10:48:36 +01:00

366 lines
12 KiB
Go

package state_accessors
import (
"fmt"
"io"
"sync"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/cl/cltypes/solid"
"github.com/ledgerwatch/erigon/ethdb/cbor"
)
// class Validator(Container):
//
// pubkey: BLSPubkey
// withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals
// effective_balance: Gwei # Balance at stake
// slashed: boolean
// # Status epochs
// activation_eligibility_epoch: Epoch # When criteria for activation were met
// activation_epoch: Epoch
// exit_epoch: Epoch
// withdrawable_epoch: Epoch # When validator can withdraw funds
//
// StaticValidator is designed to track changes in a validator's attributes over time.
// It keeps track of attributes such as withdrawal credentials, slashed status, and various epochs
// that typically change at most twice during the validator's lifespan.
type StaticValidator struct {
publicKeys []staticValidatorField[libcommon.Bytes48] // Tracks changes in public keys.
withdrawalCredentials []staticValidatorField[libcommon.Hash] // Tracks changes in withdrawal credentials.
slashed []staticValidatorField[bool] // Tracks changes in slashed status.
activationEligibility []staticValidatorField[uint64] // Tracks changes in activation eligibility epoch.
activationEpoch []staticValidatorField[uint64] // Tracks changes in activation epoch.
exitEpoch []staticValidatorField[uint64] // Tracks changes in exit epoch.
withdrawableEpoch []staticValidatorField[uint64] // Tracks changes in withdrawable epoch.
}
// NewStaticValidatorFromValidator creates a new StaticValidator from a given Validator and Slot,
// initializing the fields with the current state of the Validator at the given Slot.
func NewStaticValidatorFromValidator(v solid.Validator, slot uint64) *StaticValidator {
return &StaticValidator{
// Initializes each field with the current state of the validator.
publicKeys: []staticValidatorField[libcommon.Bytes48]{{slot, v.PublicKey()}},
withdrawalCredentials: []staticValidatorField[libcommon.Hash]{{slot, v.WithdrawalCredentials()}},
slashed: []staticValidatorField[bool]{{slot, v.Slashed()}},
activationEligibility: []staticValidatorField[uint64]{{slot, v.ActivationEligibilityEpoch()}},
activationEpoch: []staticValidatorField[uint64]{{slot, v.ActivationEpoch()}},
exitEpoch: []staticValidatorField[uint64]{{slot, v.ExitEpoch()}},
withdrawableEpoch: []staticValidatorField[uint64]{{slot, v.WithdrawableEpoch()}},
}
}
// AddWithdrawalCredentials adds a new withdrawal credential entry to the validator.
// This method is used to track changes in withdrawal credentials over time.
func (s *StaticValidator) AddWithdrawalCredentials(slot uint64, withdrawalCredentials libcommon.Hash) {
s.withdrawalCredentials = append(s.withdrawalCredentials, staticValidatorField[libcommon.Hash]{slot, withdrawalCredentials})
}
// cborStaticValidator is a struct used for CBOR serialization of StaticValidator data.
type cborStaticValidator struct {
PublicKeys []staticValidatorField[libcommon.Bytes48]
WithdrawalCredentials []staticValidatorField[libcommon.Hash]
Slashed []staticValidatorField[bool]
ActivationEligibility []staticValidatorField[uint64]
ActivationEpoch []staticValidatorField[uint64]
ExitEpoch []staticValidatorField[uint64]
WithdrawableEpoch []staticValidatorField[uint64]
}
// Serialize encodes the StaticValidator data into CBOR format and writes it to the given writer.
func (s *StaticValidator) WriteTo(w io.Writer) error {
return cbor.Marshal(w, cborStaticValidator{
PublicKeys: s.publicKeys,
WithdrawalCredentials: s.withdrawalCredentials,
Slashed: s.slashed,
ActivationEligibility: s.activationEligibility,
ActivationEpoch: s.activationEpoch,
ExitEpoch: s.exitEpoch,
WithdrawableEpoch: s.withdrawableEpoch,
})
}
// Deserialize decodes CBOR data from the given reader and updates the StaticValidator fields.
func (s *StaticValidator) ReadFrom(r io.Reader) error {
tmp := &cborStaticValidator{}
if err := cbor.Unmarshal(&tmp, r); err != nil {
return err
}
s.withdrawalCredentials = tmp.WithdrawalCredentials
s.slashed = tmp.Slashed
s.activationEligibility = tmp.ActivationEligibility
s.activationEpoch = tmp.ActivationEpoch
s.exitEpoch = tmp.ExitEpoch
s.withdrawableEpoch = tmp.WithdrawableEpoch
s.publicKeys = tmp.PublicKeys
return nil
}
func (s *StaticValidator) AddSlashed(slot uint64, slashed bool) {
s.slashed = append(s.slashed, staticValidatorField[bool]{slot, slashed})
}
func (s *StaticValidator) AddActivationEligibility(slot uint64, activationEligibility uint64) {
s.activationEligibility = append(s.activationEligibility, staticValidatorField[uint64]{slot, activationEligibility})
}
func (s *StaticValidator) AddActivationEpoch(slot uint64, activationEpoch uint64) {
s.activationEpoch = append(s.activationEpoch, staticValidatorField[uint64]{slot, activationEpoch})
}
func (s *StaticValidator) AddExitEpoch(slot uint64, exitEpoch uint64) {
s.exitEpoch = append(s.exitEpoch, staticValidatorField[uint64]{slot, exitEpoch})
}
func (s *StaticValidator) AddWithdrawableEpoch(slot uint64, withdrawableEpoch uint64) {
s.withdrawableEpoch = append(s.withdrawableEpoch, staticValidatorField[uint64]{slot, withdrawableEpoch})
}
func (s *StaticValidator) WithdrawalCredentials(slot uint64) libcommon.Hash {
currIndex := 0
for i, v := range s.withdrawalCredentials {
if v.Slot > slot {
break
}
currIndex = i
}
return s.withdrawalCredentials[currIndex].Field
}
func (s *StaticValidator) Slashed(slot uint64) bool {
currIndex := 0
for i, v := range s.slashed {
if v.Slot > slot {
break
}
currIndex = i
}
return s.slashed[currIndex].Field
}
func (s *StaticValidator) ActivationEligibilityEpoch(slot uint64) uint64 {
currIndex := 0
for i, v := range s.activationEligibility {
if v.Slot > slot {
break
}
currIndex = i
}
return s.activationEligibility[currIndex].Field
}
func (s *StaticValidator) ActivationEpoch(slot uint64) uint64 {
currIndex := 0
for i, v := range s.activationEpoch {
if v.Slot > slot {
break
}
currIndex = i
}
return s.activationEpoch[currIndex].Field
}
func (s *StaticValidator) ExitEpoch(slot uint64) uint64 {
currIndex := 0
for i, v := range s.exitEpoch {
if v.Slot > slot {
break
}
currIndex = i
}
return s.exitEpoch[currIndex].Field
}
func (s *StaticValidator) WithdrawableEpoch(slot uint64) uint64 {
currIndex := 0
for i, v := range s.withdrawableEpoch {
if v.Slot > slot {
break
}
currIndex = i
}
return s.withdrawableEpoch[currIndex].Field
}
func (s *StaticValidator) PublicKey(slot uint64) libcommon.Bytes48 {
currIndex := 0
for i, v := range s.publicKeys {
if v.Slot > slot {
break
}
currIndex = i
}
return s.publicKeys[currIndex].Field
}
func (s *StaticValidator) ToValidator(v solid.Validator, slot uint64) {
v.SetPublicKey(s.PublicKey(slot))
v.SetWithdrawalCredentials(s.WithdrawalCredentials(slot))
v.SetSlashed(s.Slashed(slot))
v.SetActivationEligibilityEpoch(s.ActivationEligibilityEpoch(slot))
v.SetActivationEpoch(s.ActivationEpoch(slot))
v.SetExitEpoch(s.ExitEpoch(slot))
v.SetWithdrawableEpoch(s.WithdrawableEpoch(slot))
}
type staticValidatorField[V any] struct {
Slot uint64
Field V
}
// StaticValidatorTable is a structure to manage a collection of StaticValidators.
// It is used for tracking multiple validators and their state changes.
type StaticValidatorTable struct {
validatorTable []*StaticValidator
slot uint64
sync sync.RWMutex // Mutex for safe concurrent access.
}
// NewStaticValidatorTable creates a new instance of StaticValidatorTable.
func NewStaticValidatorTable() *StaticValidatorTable {
return &StaticValidatorTable{
validatorTable: make([]*StaticValidator, 0, 2400), // Preallocating memory for efficiency.
}
}
func (s *StaticValidatorTable) AddValidator(v solid.Validator, validatorIndex, slot uint64) error {
s.sync.Lock()
defer s.sync.Unlock()
s.validatorTable = append(s.validatorTable, NewStaticValidatorFromValidator(v, slot))
if validatorIndex >= uint64(len(s.validatorTable)) {
return fmt.Errorf("validator index mismatch")
}
return nil
}
func (s *StaticValidatorTable) AddWithdrawalCredentials(validatorIndex, slot uint64, withdrawalCredentials libcommon.Hash) error {
s.sync.Lock()
defer s.sync.Unlock()
if validatorIndex >= uint64(len(s.validatorTable)) {
return fmt.Errorf("validator index mismatch")
}
s.validatorTable[validatorIndex].AddWithdrawalCredentials(slot, withdrawalCredentials)
return nil
}
func (s *StaticValidatorTable) AddSlashed(validatorIndex, slot uint64, slashed bool) error {
s.sync.Lock()
defer s.sync.Unlock()
if validatorIndex >= uint64(len(s.validatorTable)) {
return fmt.Errorf("validator index mismatch")
}
s.validatorTable[validatorIndex].AddSlashed(slot, slashed)
return nil
}
func (s *StaticValidatorTable) AddActivationEligibility(validatorIndex, slot uint64, activationEligibility uint64) error {
s.sync.Lock()
defer s.sync.Unlock()
if validatorIndex >= uint64(len(s.validatorTable)) {
return fmt.Errorf("validator index mismatch")
}
s.validatorTable[validatorIndex].AddActivationEligibility(slot, activationEligibility)
return nil
}
func (s *StaticValidatorTable) AddActivationEpoch(validatorIndex, slot uint64, activationEpoch uint64) error {
s.sync.Lock()
defer s.sync.Unlock()
if validatorIndex >= uint64(len(s.validatorTable)) {
return fmt.Errorf("validator index mismatch")
}
s.validatorTable[validatorIndex].AddActivationEpoch(slot, activationEpoch)
return nil
}
func (s *StaticValidatorTable) AddExitEpoch(validatorIndex, slot uint64, exitEpoch uint64) error {
s.sync.Lock()
defer s.sync.Unlock()
if validatorIndex >= uint64(len(s.validatorTable)) {
return fmt.Errorf("validator index mismatch")
}
s.validatorTable[validatorIndex].AddExitEpoch(slot, exitEpoch)
return nil
}
func (s *StaticValidatorTable) AddWithdrawableEpoch(validatorIndex, slot uint64, withdrawableEpoch uint64) error {
s.sync.Lock()
defer s.sync.Unlock()
if validatorIndex >= uint64(len(s.validatorTable)) {
return fmt.Errorf("validator index mismatch")
}
s.validatorTable[validatorIndex].AddWithdrawableEpoch(slot, withdrawableEpoch)
return nil
}
func (s *StaticValidatorTable) GetInPlace(validatorIndex uint64, slot uint64, v solid.Validator) {
s.sync.RLock()
defer s.sync.RUnlock()
s.validatorTable[validatorIndex].ToValidator(v, slot)
}
func (s *StaticValidatorTable) ForEach(fn func(validatorIndex uint64, validator *StaticValidator) bool) {
s.sync.RLock()
defer s.sync.RUnlock()
for i, v := range s.validatorTable {
if !fn(uint64(i), v) {
break
}
}
}
func (s *StaticValidatorTable) WithdrawalCredentials(validatorIndex uint64, slot uint64) libcommon.Hash {
s.sync.RLock()
defer s.sync.RUnlock()
return s.validatorTable[validatorIndex].WithdrawalCredentials(slot)
}
func (s *StaticValidatorTable) Slashed(validatorIndex uint64, slot uint64) bool {
s.sync.RLock()
defer s.sync.RUnlock()
return s.validatorTable[validatorIndex].Slashed(slot)
}
func (s *StaticValidatorTable) ActivationEligibilityEpoch(validatorIndex uint64, slot uint64) uint64 {
s.sync.RLock()
defer s.sync.RUnlock()
return s.validatorTable[validatorIndex].ActivationEligibilityEpoch(slot)
}
func (s *StaticValidatorTable) ActivationEpoch(validatorIndex uint64, slot uint64) uint64 {
s.sync.RLock()
defer s.sync.RUnlock()
return s.validatorTable[validatorIndex].ActivationEpoch(slot)
}
func (s *StaticValidatorTable) ExitEpoch(validatorIndex uint64, slot uint64) uint64 {
s.sync.RLock()
defer s.sync.RUnlock()
return s.validatorTable[validatorIndex].ExitEpoch(slot)
}
func (s *StaticValidatorTable) WithdrawableEpoch(validatorIndex uint64, slot uint64) uint64 {
s.sync.RLock()
defer s.sync.RUnlock()
return s.validatorTable[validatorIndex].WithdrawableEpoch(slot)
}
func (s *StaticValidatorTable) GetStaticValidator(validatorIndex uint64) *StaticValidator {
s.sync.RLock()
defer s.sync.RUnlock()
return s.validatorTable[validatorIndex]
}
// This is for versioning
func (s *StaticValidatorTable) SetSlot(slot uint64) {
s.sync.Lock()
defer s.sync.Unlock()
s.slot = slot
}
func (s *StaticValidatorTable) Slot() uint64 {
s.sync.RLock()
defer s.sync.RUnlock()
return s.slot
}