mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
202 lines
5.7 KiB
Go
202 lines
5.7 KiB
Go
package beacon
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"database/sql"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/ethereum/go-ethereum/metrics"
|
|
"github.com/holiman/uint256"
|
|
"github.com/protolambda/zrnt/eth2/beacon/common"
|
|
"github.com/protolambda/ztyp/codec"
|
|
|
|
"github.com/ethereum/go-ethereum/portalnetwork/storage"
|
|
)
|
|
|
|
const BytesInMB uint64 = 1000 * 1000
|
|
|
|
type BeaconStorage struct {
|
|
storageCapacityInBytes uint64
|
|
db *sql.DB
|
|
log log.Logger
|
|
spec *common.Spec
|
|
cache *beaconStorageCache
|
|
}
|
|
|
|
var portalStorageMetrics *metrics.PortalStorageMetrics
|
|
|
|
type beaconStorageCache struct {
|
|
OptimisticUpdate []byte
|
|
FinalityUpdate []byte
|
|
}
|
|
|
|
var _ storage.ContentStorage = &BeaconStorage{}
|
|
|
|
func NewBeaconStorage(config storage.PortalStorageConfig) (storage.ContentStorage, error) {
|
|
bs := &BeaconStorage{
|
|
storageCapacityInBytes: config.StorageCapacityMB * BytesInMB,
|
|
db: config.DB,
|
|
log: log.New("beacon_storage"),
|
|
spec: config.Spec,
|
|
cache: &beaconStorageCache{},
|
|
}
|
|
if err := bs.setup(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var err error
|
|
portalStorageMetrics, err = metrics.NewPortalStorageMetrics(config.NetworkName, config.DB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bs, nil
|
|
}
|
|
|
|
func (bs *BeaconStorage) setup() error {
|
|
if _, err := bs.db.Exec(CreateQueryDBBeacon); err != nil {
|
|
return err
|
|
}
|
|
if _, err := bs.db.Exec(LCUpdateCreateTable); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (bs *BeaconStorage) Get(contentKey []byte, contentId []byte) ([]byte, error) {
|
|
switch storage.ContentType(contentKey[0]) {
|
|
case LightClientBootstrap:
|
|
return bs.getContentValue(contentId)
|
|
case LightClientUpdate:
|
|
lightClientUpdateKey := new(LightClientUpdateKey)
|
|
err := lightClientUpdateKey.UnmarshalSSZ(contentKey[1:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bs.getLcUpdateValueByRange(lightClientUpdateKey.StartPeriod, lightClientUpdateKey.StartPeriod+lightClientUpdateKey.Count)
|
|
case LightClientFinalityUpdate:
|
|
if bs.cache.FinalityUpdate == nil {
|
|
return nil, storage.ErrContentNotFound
|
|
}
|
|
return bs.cache.FinalityUpdate, nil
|
|
case LightClientOptimisticUpdate:
|
|
if bs.cache.OptimisticUpdate == nil {
|
|
return nil, storage.ErrContentNotFound
|
|
}
|
|
return bs.cache.OptimisticUpdate, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (bs *BeaconStorage) Put(contentKey []byte, contentId []byte, content []byte) error {
|
|
switch storage.ContentType(contentKey[0]) {
|
|
case LightClientBootstrap:
|
|
return bs.putContentValue(contentId, contentKey, content)
|
|
case LightClientUpdate:
|
|
lightClientUpdateKey := new(LightClientUpdateKey)
|
|
err := lightClientUpdateKey.UnmarshalSSZ(contentKey[1:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lightClientUpdateRange := new(LightClientUpdateRange)
|
|
reader := codec.NewDecodingReader(bytes.NewReader(content), uint64(len(content)))
|
|
err = lightClientUpdateRange.Deserialize(bs.spec, reader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for index, update := range *lightClientUpdateRange {
|
|
var buf bytes.Buffer
|
|
writer := codec.NewEncodingWriter(&buf)
|
|
err := update.Serialize(bs.spec, writer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
period := lightClientUpdateKey.StartPeriod + uint64(index)
|
|
err = bs.putLcUpdate(period, buf.Bytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
case LightClientFinalityUpdate:
|
|
bs.cache.FinalityUpdate = content
|
|
return nil
|
|
case LightClientOptimisticUpdate:
|
|
bs.cache.OptimisticUpdate = content
|
|
return nil
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (bs *BeaconStorage) Radius() *uint256.Int {
|
|
return storage.MaxDistance
|
|
}
|
|
|
|
func (bs *BeaconStorage) getContentValue(contentId []byte) ([]byte, error) {
|
|
res := make([]byte, 0)
|
|
err := bs.db.QueryRowContext(context.Background(), ContentValueLookupQueryBeacon, contentId).Scan(&res)
|
|
if err == sql.ErrNoRows {
|
|
return nil, storage.ErrContentNotFound
|
|
}
|
|
return res, err
|
|
}
|
|
|
|
func (bs *BeaconStorage) getLcUpdateValueByRange(start, end uint64) ([]byte, error) {
|
|
// LightClientUpdateRange := make([]ForkedLightClientUpdate, 0)
|
|
var lightClientUpdateRange LightClientUpdateRange
|
|
rows, err := bs.db.QueryContext(context.Background(), LCUpdateLookupQueryByRange, start, end)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hasData := false
|
|
defer func(rows *sql.Rows) {
|
|
err = rows.Close()
|
|
if err != nil {
|
|
bs.log.Error("failed to close rows", "err", err)
|
|
}
|
|
}(rows)
|
|
for rows.Next() {
|
|
hasData = true
|
|
var val []byte
|
|
err = rows.Scan(&val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
update := new(ForkedLightClientUpdate)
|
|
dec := codec.NewDecodingReader(bytes.NewReader(val), uint64(len(val)))
|
|
err = update.Deserialize(bs.spec, dec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lightClientUpdateRange = append(lightClientUpdateRange, *update)
|
|
}
|
|
if !hasData {
|
|
return nil, storage.ErrContentNotFound
|
|
}
|
|
var buf bytes.Buffer
|
|
err = lightClientUpdateRange.Serialize(bs.spec, codec.NewEncodingWriter(&buf))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func (bs *BeaconStorage) putContentValue(contentId, contentKey, value []byte) error {
|
|
length := 32 + len(contentKey) + len(value)
|
|
_, err := bs.db.ExecContext(context.Background(), InsertQueryBeacon, contentId, contentKey, value, length)
|
|
if metrics.Enabled && err != nil {
|
|
portalStorageMetrics.EntriesCount.Inc(1)
|
|
portalStorageMetrics.ContentStorageUsage.Inc(int64(len(value)))
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (bs *BeaconStorage) putLcUpdate(period uint64, value []byte) error {
|
|
_, err := bs.db.ExecContext(context.Background(), InsertLCUpdateQuery, period, value, 0, len(value))
|
|
if metrics.Enabled && err != nil {
|
|
portalStorageMetrics.EntriesCount.Inc(1)
|
|
portalStorageMetrics.ContentStorageUsage.Inc(int64(len(value)))
|
|
}
|
|
return err
|
|
}
|