erigon-pulse/cl/persistence/block_saver.go
2023-10-18 23:10:53 +02:00

159 lines
4.4 KiB
Go

package persistence
import (
"context"
"fmt"
"io"
"path"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cl/persistence/beacon_indicies"
"github.com/ledgerwatch/erigon/cl/phase1/execution_client"
"github.com/ledgerwatch/erigon/cl/sentinel/peers"
"github.com/ledgerwatch/erigon/cmd/sentinel/sentinel/communication/ssz_snappy"
"github.com/spf13/afero"
)
type beaconChainDatabaseFilesystem struct {
rawDB RawBeaconBlockChain
cfg *clparams.BeaconChainConfig
executionEngine execution_client.ExecutionEngine
}
func NewBeaconChainDatabaseFilesystem(rawDB RawBeaconBlockChain, executionEngine execution_client.ExecutionEngine, cfg *clparams.BeaconChainConfig) BeaconChainDatabase {
return beaconChainDatabaseFilesystem{
rawDB: rawDB,
cfg: cfg,
executionEngine: executionEngine,
}
}
func (b beaconChainDatabaseFilesystem) GetRange(tx kv.Tx, ctx context.Context, from uint64, count uint64) ([]*peers.PeeredObject[*cltypes.SignedBeaconBlock], error) {
// Retrieve block roots for each ranged slot
beaconBlockRooots, slots, err := beacon_indicies.ReadBeaconBlockRootsInSlotRange(ctx, tx, from, count)
if err != nil {
return nil, err
}
if len(beaconBlockRooots) == 0 {
return nil, nil
}
blocks := []*peers.PeeredObject[*cltypes.SignedBeaconBlock]{}
for idx, blockRoot := range beaconBlockRooots {
slot := slots[idx]
r, err := b.rawDB.BlockReader(ctx, slot, blockRoot)
if err != nil {
return nil, err
}
defer r.Close()
block := cltypes.NewSignedBeaconBlock(b.cfg)
version := b.cfg.GetCurrentStateVersion(slot / b.cfg.SlotsPerEpoch)
if err := ssz_snappy.DecodeAndReadNoForkDigest(r, block, version); err != nil {
return nil, err
}
blocks = append(blocks, &peers.PeeredObject[*cltypes.SignedBeaconBlock]{Data: block})
}
return blocks, nil
}
func (b beaconChainDatabaseFilesystem) PurgeRange(tx kv.RwTx, ctx context.Context, from uint64, count uint64) error {
if err := beacon_indicies.RangeBlockRoots(ctx, tx, from, from+count, func(slot uint64, beaconBlockRoot libcommon.Hash) bool {
b.rawDB.DeleteBlock(ctx, slot, beaconBlockRoot)
return true
}); err != nil {
return err
}
return beacon_indicies.PruneBlockRoots(ctx, tx, from, from+count)
}
func (b beaconChainDatabaseFilesystem) WriteBlock(tx kv.RwTx, ctx context.Context, block *cltypes.SignedBeaconBlock, canonical bool) error {
blockRoot, err := block.Block.HashSSZ()
if err != nil {
return err
}
w, err := b.rawDB.BlockWriter(ctx, block.Block.Slot, blockRoot)
if err != nil {
return err
}
defer w.Close()
if fp, ok := w.(afero.File); ok {
err = fp.Truncate(0)
if err != nil {
return err
}
_, err = fp.Seek(0, io.SeekStart)
if err != nil {
return err
}
}
err = ssz_snappy.EncodeAndWrite(w, block)
if err != nil {
return err
}
if fp, ok := w.(afero.File); ok {
err = fp.Sync()
if err != nil {
return err
}
}
bodyRoot, err := block.Block.Body.HashSSZ()
if err != nil {
return err
}
if err := beacon_indicies.WriteBeaconBlockHeaderAndIndicies(ctx, tx, &cltypes.SignedBeaconBlockHeader{
Signature: block.Signature,
Header: &cltypes.BeaconBlockHeader{
Slot: block.Block.Slot,
ParentRoot: block.Block.ParentRoot,
ProposerIndex: block.Block.ProposerIndex,
Root: block.Block.StateRoot,
BodyRoot: bodyRoot,
},
}, canonical); err != nil {
return err
}
return nil
}
// SlotToPaths define the file structure to store a block
//
// superEpoch = floor(slot / (epochSize ^ 2))
// epoch = floot(slot / epochSize)
// file is to be stored at
// "/signedBeaconBlock/{superEpoch}/{epoch}/{root}.ssz_snappy"
func RootToPaths(slot uint64, root libcommon.Hash, config *clparams.BeaconChainConfig) (folderPath string, filePath string) {
folderPath = path.Clean(fmt.Sprintf("%d/%d", slot/(config.SlotsPerEpoch*config.SlotsPerEpoch), slot/config.SlotsPerEpoch))
filePath = path.Clean(fmt.Sprintf("%s/%x.sz", folderPath, root))
return
}
func ValidateEpoch(fs afero.Fs, epoch uint64, config *clparams.BeaconChainConfig) error {
superEpoch := epoch / (config.SlotsPerEpoch)
// the folder path is superEpoch/epoch
folderPath := path.Clean(fmt.Sprintf("%d/%d", superEpoch, epoch))
fi, err := afero.ReadDir(fs, folderPath)
if err != nil {
return err
}
for _, fn := range fi {
fn.Name()
}
return nil
}