Skip to content

Commit

Permalink
Caplin: remove merkle tree disk-based caching (#13671)
Browse files Browse the repository at this point in the history
This is a simplification. Instead keep the state at `head-1` in memory
for reorgs. overall less I/O and less RAM + faster reorg speed
  • Loading branch information
Giulio2002 authored Feb 5, 2025
1 parent bdc8b79 commit e76c827
Show file tree
Hide file tree
Showing 28 changed files with 191 additions and 473 deletions.
1 change: 1 addition & 0 deletions cl/beacon/synced_data/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type SyncedData interface {
OnHeadState(newState *state.CachingBeaconState) error
UnsetHeadState()
ViewHeadState(fn ViewHeadStateFn) error
ViewPreviousHeadState(fn ViewHeadStateFn) error
Syncing() bool
HeadSlot() uint64
HeadRoot() common.Hash
Expand Down
38 changes: 38 additions & 0 deletions cl/beacon/synced_data/mock_services/synced_data_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 34 additions & 5 deletions cl/beacon/synced_data/synced_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import (
"github.com/erigontech/erigon/cl/phase1/core/state"
)

var ErrNotSynced = errors.New("not synced")
var (
ErrNotSynced = errors.New("not synced")
ErrPreviousStateNotAvailable = errors.New("previous state not available")
)

var _ SyncedData = (*SyncedDataManager)(nil)

Expand All @@ -43,11 +46,11 @@ type SyncedDataManager struct {
headRoot atomic.Value
headSlot atomic.Uint64

headState *state.CachingBeaconState
headState *state.CachingBeaconState
previousHeadState *state.CachingBeaconState

accessLock sync.RWMutex // lock used for accessing atomic methods

mu sync.RWMutex
mu sync.RWMutex
}

func NewSyncedDataManager(cfg *clparams.BeaconChainConfig, enabled bool) *SyncedDataManager {
Expand All @@ -57,6 +60,7 @@ func NewSyncedDataManager(cfg *clparams.BeaconChainConfig, enabled bool) *Synced
}
}

// OnHeadState updates the current head state and tracks the previous state.
func (s *SyncedDataManager) OnHeadState(newState *state.CachingBeaconState) (err error) {
if !s.enabled {
return
Expand All @@ -67,8 +71,21 @@ func (s *SyncedDataManager) OnHeadState(newState *state.CachingBeaconState) (err
s.accessLock.Lock()
defer s.accessLock.Unlock()

// Save current state as previous state, if available.
if s.headState != nil {
if s.previousHeadState != nil {
err = s.headState.CopyInto(s.previousHeadState)
} else {
s.previousHeadState, err = s.headState.Copy()
}
if err != nil {
return err
}
}

var blkRoot common.Hash

// Update headState with the new state.
if s.headState == nil {
s.headState, err = newState.Copy()
} else {
Expand All @@ -83,9 +100,10 @@ func (s *SyncedDataManager) OnHeadState(newState *state.CachingBeaconState) (err
}
s.headSlot.Store(newState.Slot())
s.headRoot.Store(blkRoot)
return err
return nil
}

// ViewHeadState allows safe, read-only access to the current head state.
func (s *SyncedDataManager) ViewHeadState(fn ViewHeadStateFn) error {
_, synced := s.headRoot.Load().(common.Hash)
if !s.enabled || !synced {
Expand All @@ -112,6 +130,16 @@ func (s *SyncedDataManager) ViewHeadState(fn ViewHeadStateFn) error {
return nil
}

// ViewPreviousHeadState allows safe, read-only access to the previous head state.
func (s *SyncedDataManager) ViewPreviousHeadState(fn ViewHeadStateFn) error {
s.mu.RLock()
defer s.mu.RUnlock()
if s.previousHeadState == nil {
return ErrPreviousStateNotAvailable
}
return fn(s.previousHeadState)
}

func (s *SyncedDataManager) Syncing() bool {
_, synced := s.headRoot.Load().(common.Hash)
return !synced
Expand Down Expand Up @@ -147,6 +175,7 @@ func (s *SyncedDataManager) UnsetHeadState() {
s.headRoot = atomic.Value{}
s.headSlot.Store(uint64(0))
s.headState = nil
s.previousHeadState = nil
}

func (s *SyncedDataManager) ValidatorPublicKeyByIndex(index int) (common.Bytes48, error) {
Expand Down
5 changes: 2 additions & 3 deletions cl/clparams/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,8 @@ const (

var (
MainnetBootstrapNodes = []string{
// Teku team's bootnode
"enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA",
"enr:-KG4QL-eqFoHy0cI31THvtZjpYUu_Jdw_MO7skQRJxY1g5HTN1A0epPCU6vi0gLGUgrzpU-ygeMSS8ewVxDpKfYmxMMGhGV0aDKQtTA_KgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaED8GJ2vzUqgL6-KD1xalo1CsmY4X1HaDnyl6Y_WayCo9GDdGNwgiMog3VkcIIjKA",
"enr:-KG4QNTx85fjxABbSq_Rta9wy56nQ1fHK0PewJbGjLm1M4bMGx5-3Qq4ZX2-iFJ0pys_O90sVXNNOxp2E7afBsGsBrgDhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQEnfA2iXNlY3AyNTZrMaECGXWQ-rQ2KZKRH1aOW4IlPDBkY4XDphxg9pxKytFCkayDdGNwgiMog3VkcIIjKA",
"enr:-KG4QF4B5WrlFcRhUU6dZETwY5ZzAXnA0vGC__L1Kdw602nDZwXSTs5RFXFIFUnbQJmhNGVU6OIX7KVrCSTODsz1tK4DhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQExNYEiXNlY3AyNTZrMaECQmM9vp7KhaXhI-nqL_R0ovULLCFSFTa9CPPSdb1zPX6DdGNwgiMog3VkcIIjKA",
// Prylab team's bootnodes
"enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg",
"enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA",
Expand Down
22 changes: 0 additions & 22 deletions cl/cltypes/solid/hash_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package solid

import (
"encoding/json"
"io"

libcommon "github.com/erigontech/erigon-lib/common"
"github.com/erigontech/erigon-lib/common/length"
Expand Down Expand Up @@ -190,24 +189,3 @@ func (h *hashList) Range(fn func(int, libcommon.Hash, int) bool) {
func (h *hashList) Pop() libcommon.Hash {
panic("didnt ask, dont need it, go fuck yourself")
}

func (h *hashList) ReadMerkleTree(r io.Reader) error {
if h.MerkleTree == nil {
h.MerkleTree = &merkle_tree.MerkleTree{}
h.MerkleTree.Initialize(h.l, merkle_tree.OptimalMaxTreeCacheDepth, func(idx int, out []byte) {
copy(out, h.u[idx*length.Hash:(idx+1)*length.Hash])
}, /*limit=*/ nil)
}
return h.MerkleTree.ReadMerkleTree(r)
}

func (h *hashList) WriteMerkleTree(w io.Writer) error {
if h.MerkleTree == nil {
cap := uint64(h.c)
h.MerkleTree = &merkle_tree.MerkleTree{}
h.MerkleTree.Initialize(h.l, merkle_tree.OptimalMaxTreeCacheDepth, func(idx int, out []byte) {
copy(out, h.u[idx*length.Hash:(idx+1)*length.Hash])
}, /*limit=*/ &cap)
}
return h.MerkleTree.WriteMerkleTree(w)
}
9 changes: 0 additions & 9 deletions cl/cltypes/solid/hash_vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package solid

import (
"encoding/json"
"io"

libcommon "github.com/erigontech/erigon-lib/common"
"github.com/erigontech/erigon-lib/common/length"
Expand Down Expand Up @@ -118,11 +117,3 @@ func (h *hashVector) Range(fn func(int, libcommon.Hash, int) bool) {
func (h *hashVector) Pop() libcommon.Hash {
panic("didnt ask, dont need it, go fuck yourself")
}

func (h *hashVector) ReadMerkleTree(r io.Reader) error {
return h.u.ReadMerkleTree(r)
}

func (h *hashVector) WriteMerkleTree(w io.Writer) error {
return h.u.WriteMerkleTree(w)
}
9 changes: 0 additions & 9 deletions cl/cltypes/solid/uint64_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package solid

import (
"encoding/json"
"io"

"github.com/erigontech/erigon-lib/types/clonable"
)
Expand Down Expand Up @@ -119,14 +118,6 @@ func (arr *uint64ListSSZ) Append(v uint64) {
arr.u.Append(v)
}

func (arr *uint64ListSSZ) ReadMerkleTree(r io.Reader) error {
return arr.u.ReadMerkleTree(r)
}

func (arr *uint64ListSSZ) WriteMerkleTree(w io.Writer) error {
return arr.u.WriteMerkleTree(w)
}

// Check if it is sorted and check if there are duplicates. O(N) complexity.
func IsUint64SortedSet(set IterableSSZ[uint64]) bool {
for i := 0; i < set.Length()-1; i++ {
Expand Down
9 changes: 0 additions & 9 deletions cl/cltypes/solid/uint64_vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package solid

import (
"encoding/json"
"io"

"github.com/erigontech/erigon-lib/types/clonable"
)
Expand Down Expand Up @@ -110,11 +109,3 @@ func (arr *uint64VectorSSZ) Pop() uint64 {
func (arr *uint64VectorSSZ) Append(uint64) {
panic("not implemented")
}

func (arr *uint64VectorSSZ) ReadMerkleTree(r io.Reader) error {
return arr.u.ReadMerkleTree(r)
}

func (arr *uint64VectorSSZ) WriteMerkleTree(w io.Writer) error {
return arr.u.WriteMerkleTree(w)
}
22 changes: 0 additions & 22 deletions cl/cltypes/solid/uint64slice_byte.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package solid
import (
"encoding/binary"
"encoding/json"
"io"
"strconv"

"github.com/erigontech/erigon-lib/common/length"
Expand Down Expand Up @@ -229,24 +228,3 @@ func (arr *byteBasedUint64Slice) DecodeSSZ(buf []byte, _ int) error {
func (arr *byteBasedUint64Slice) EncodingSizeSSZ() int {
return arr.l * 8
}

func (arr *byteBasedUint64Slice) ReadMerkleTree(r io.Reader) error {
if arr.MerkleTree == nil {
arr.MerkleTree = &merkle_tree.MerkleTree{}
arr.MerkleTree.Initialize((arr.l+3)/4, merkle_tree.OptimalMaxTreeCacheDepth, func(idx int, out []byte) {
copy(out, arr.u[idx*length.Hash:])
}, nil)
}
return arr.MerkleTree.ReadMerkleTree(r)
}

func (arr *byteBasedUint64Slice) WriteMerkleTree(w io.Writer) error {
if arr.MerkleTree == nil {
arr.MerkleTree = &merkle_tree.MerkleTree{}
cap := uint64((arr.c*8 + length.Hash - 1) / length.Hash)
arr.MerkleTree.Initialize((arr.l+3)/4, merkle_tree.OptimalMaxTreeCacheDepth, func(idx int, out []byte) {
copy(out, arr.u[idx*length.Hash:])
}, &cap)
}
return arr.MerkleTree.WriteMerkleTree(w)
}
46 changes: 11 additions & 35 deletions cl/cltypes/solid/validator_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package solid

import (
"encoding/json"
"io"

libcommon "github.com/erigontech/erigon-lib/common"
"github.com/erigontech/erigon-lib/types/clonable"
Expand Down Expand Up @@ -141,14 +140,23 @@ func (v *ValidatorSet) CopyTo(t *ValidatorSet) {
t.MerkleTree = &merkle_tree.MerkleTree{}
}
v.MerkleTree.CopyInto(t.MerkleTree)

hashBuffer := make([]byte, 8*32)
t.MerkleTree.SetComputeLeafFn(func(idx int, out []byte) {
copy(out, t.buffer[idx*validatorSize:])
validator := t.Get(idx)
if err := validator.CopyHashBufferTo(hashBuffer); err != nil {
panic(err)
}
hashBuffer = hashBuffer[:(8 * 32)]
if err := merkle_tree.MerkleRootFromFlatLeaves(hashBuffer, out); err != nil {
panic(err)
}
})
} else {
t.MerkleTree = nil
}
// skip copying (unsupported for phase0)
t.phase0Data = make([]Phase0Data, t.l)
t.phase0Data = make([]Phase0Data, v.l)
copy(t.buffer, v.buffer)
copy(t.attesterBits, v.attesterBits)
t.attesterBits = t.attesterBits[:v.l]
Expand Down Expand Up @@ -377,35 +385,3 @@ func (v *ValidatorSet) UnmarshalJSON(data []byte) error {
}
return nil
}

func (v *ValidatorSet) ReadMerkleTree(r io.Reader) error {
if v.MerkleTree == nil {
v.MerkleTree = &merkle_tree.MerkleTree{}
hashBuffer := make([]byte, 8*32)
v.MerkleTree.Initialize(v.l, merkle_tree.OptimalMaxTreeCacheDepth, func(idx int, out []byte) {
validator := v.Get(idx)
if err := validator.CopyHashBufferTo(hashBuffer); err != nil {
panic(err)
}
hashBuffer = hashBuffer[:(8 * 32)]
if err := merkle_tree.MerkleRootFromFlatLeaves(hashBuffer, out); err != nil {
panic(err)
}
}, nil)
}
return v.MerkleTree.ReadMerkleTree(r)
}

func (arr *ValidatorSet) WriteMerkleTree(w io.Writer) error {
if arr.MerkleTree == nil {
arr.MerkleTree = &merkle_tree.MerkleTree{}
cap := uint64(arr.c)
arr.MerkleTree.Initialize(arr.l, merkle_tree.OptimalMaxTreeCacheDepth, func(idx int, out []byte) {
validator := arr.Get(idx)
if err := validator.CopyHashBufferTo(out); err != nil {
panic(err)
}
}, &cap)
}
return arr.MerkleTree.WriteMerkleTree(w)
}
Loading

0 comments on commit e76c827

Please sign in to comment.