mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-29 16:13:47 +00:00
703 lines
22 KiB
Go
703 lines
22 KiB
Go
package beacon
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/protolambda/zrnt/eth2/beacon/altair"
|
|
"github.com/protolambda/zrnt/eth2/beacon/capella"
|
|
"github.com/protolambda/zrnt/eth2/beacon/common"
|
|
"github.com/protolambda/zrnt/eth2/beacon/deneb"
|
|
"github.com/protolambda/zrnt/eth2/configs"
|
|
"github.com/protolambda/zrnt/eth2/util/merkle"
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
blsu "github.com/protolambda/bls12-381-util"
|
|
"github.com/protolambda/ztyp/tree"
|
|
"github.com/protolambda/ztyp/view"
|
|
)
|
|
|
|
var (
|
|
ErrInsufficientParticipation = errors.New("insufficient participation")
|
|
ErrInvalidTimestamp = errors.New("invalid timestamp")
|
|
ErrInvalidPeriod = errors.New("invalid sync committee period")
|
|
ErrNotRelevant = errors.New("update not relevant")
|
|
ErrInvalidFinalityProof = errors.New("invalid finality proof")
|
|
ErrInvalidNextSyncCommitteeProof = errors.New("invalid next sync committee proof")
|
|
ErrInvalidSignature = errors.New("invalid sync committee signature")
|
|
)
|
|
|
|
type ConsensusAPI interface {
|
|
GetBootstrap(blockRoot common.Root) (common.SpecObj, error)
|
|
GetUpdates(firstPeriod, count uint64) ([]common.SpecObj, error)
|
|
GetFinalityUpdate() (common.SpecObj, error)
|
|
GetOptimisticUpdate() (common.SpecObj, error)
|
|
ChainID() uint64
|
|
Name() string
|
|
}
|
|
|
|
type LightClientStore struct {
|
|
FinalizedHeader *common.BeaconBlockHeader
|
|
CurrentSyncCommittee *common.SyncCommittee
|
|
NextSyncCommittee *common.SyncCommittee
|
|
OptimisticHeader *common.BeaconBlockHeader
|
|
PreviousMaxActiveParticipants view.Uint64View
|
|
CurrentMaxActiveParticipants view.Uint64View
|
|
}
|
|
|
|
type ConsensusLightClient struct {
|
|
Store LightClientStore
|
|
API ConsensusAPI
|
|
InitialCheckpoint common.Root
|
|
LastCheckpoint common.Root
|
|
Config *Config
|
|
Logger log.Logger
|
|
}
|
|
|
|
type Config struct {
|
|
ConsensusAPI string
|
|
Port uint64
|
|
DefaultCheckpoint common.Root
|
|
Checkpoint common.Root
|
|
DataDir string
|
|
Chain ChainConfig
|
|
Spec *common.Spec
|
|
MaxCheckpointAge uint64
|
|
Fallback string
|
|
LoadExternalFallback bool
|
|
StrictCheckpointAge bool
|
|
}
|
|
|
|
type ChainConfig struct {
|
|
ChainID uint64
|
|
GenesisTime uint64
|
|
GenesisRoot common.Root
|
|
}
|
|
|
|
type GenericUpdate struct {
|
|
AttestedHeader *common.BeaconBlockHeader
|
|
SyncAggregate *altair.SyncAggregate
|
|
SignatureSlot common.Slot
|
|
NextSyncCommittee *common.SyncCommittee
|
|
NextSyncCommitteeBranch *altair.SyncCommitteeProofBranch
|
|
FinalizedHeader *common.BeaconBlockHeader
|
|
FinalityBranch *altair.FinalizedRootProofBranch
|
|
}
|
|
|
|
type GenericBootstrap struct {
|
|
Header *common.BeaconBlockHeader
|
|
CurrentSyncCommittee common.SyncCommittee
|
|
CurrentSyncCommitteeBranch altair.SyncCommitteeProofBranch
|
|
}
|
|
|
|
func FromBootstrap(commonBootstrap common.SpecObj) (*GenericBootstrap, error) {
|
|
switch bootstrap := commonBootstrap.(type) {
|
|
case *deneb.LightClientBootstrap:
|
|
return &GenericBootstrap{
|
|
Header: &bootstrap.Header.Beacon,
|
|
CurrentSyncCommittee: bootstrap.CurrentSyncCommittee,
|
|
CurrentSyncCommitteeBranch: bootstrap.CurrentSyncCommitteeBranch,
|
|
}, nil
|
|
case *capella.LightClientBootstrap:
|
|
return &GenericBootstrap{
|
|
Header: &bootstrap.Header.Beacon,
|
|
CurrentSyncCommittee: bootstrap.CurrentSyncCommittee,
|
|
CurrentSyncCommitteeBranch: bootstrap.CurrentSyncCommitteeBranch,
|
|
}, nil
|
|
case *altair.LightClientBootstrap:
|
|
return &GenericBootstrap{
|
|
Header: &bootstrap.Header.Beacon,
|
|
CurrentSyncCommittee: bootstrap.CurrentSyncCommittee,
|
|
CurrentSyncCommitteeBranch: bootstrap.CurrentSyncCommitteeBranch,
|
|
}, nil
|
|
}
|
|
return nil, errors.New("unknown bootstrap type")
|
|
}
|
|
|
|
func NewConsensusLightClient(api ConsensusAPI, config *Config, checkpointBlockRoot common.Root, logger log.Logger) (*ConsensusLightClient, error) {
|
|
client := &ConsensusLightClient{
|
|
API: api,
|
|
Config: config,
|
|
Logger: logger,
|
|
InitialCheckpoint: checkpointBlockRoot,
|
|
}
|
|
|
|
err := client.bootstrap()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return client, nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) GetHeader() *common.BeaconBlockHeader {
|
|
return c.Store.OptimisticHeader
|
|
}
|
|
|
|
func (c *ConsensusLightClient) GetFinalityHeader() *common.BeaconBlockHeader {
|
|
return c.Store.FinalizedHeader
|
|
}
|
|
|
|
func (c *ConsensusLightClient) Sync() error {
|
|
err := c.bootstrap()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bootstrapPeriod := CalcSyncPeriod(uint64(c.Store.FinalizedHeader.Slot))
|
|
|
|
updates := make([]common.SpecObj, 0)
|
|
|
|
if c.API.Name() == "portal" {
|
|
currentPeriod := CalcSyncPeriod(uint64(c.expectedCurrentSlot()))
|
|
for i := bootstrapPeriod; i < currentPeriod; i++ {
|
|
update, err := c.API.GetUpdates(i, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
updates = append(updates, update...)
|
|
}
|
|
} else {
|
|
updates, err = c.API.GetUpdates(bootstrapPeriod, MaxRequestLightClientUpdates)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for _, update := range updates {
|
|
err = c.VerifyUpdate(update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.ApplyUpdate(update)
|
|
}
|
|
|
|
finalityUpdate, err := c.API.GetFinalityUpdate()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = c.VerifyFinalityUpdate(finalityUpdate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.ApplyFinalityUpdate(finalityUpdate)
|
|
|
|
optimisticUpdate, err := c.API.GetOptimisticUpdate()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = c.VerifyOptimisticUpdate(optimisticUpdate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.ApplyOptimisticUpdate(optimisticUpdate)
|
|
|
|
c.Logger.Info("Light client in sync with ", "checkpoint", hexutil.Encode(c.InitialCheckpoint[:]))
|
|
return nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) Advance() error {
|
|
finalityUpdate, err := c.API.GetFinalityUpdate()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = c.VerifyFinalityUpdate(finalityUpdate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.ApplyFinalityUpdate(finalityUpdate)
|
|
|
|
optimisticUpdate, err := c.API.GetOptimisticUpdate()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = c.VerifyOptimisticUpdate(optimisticUpdate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.ApplyOptimisticUpdate(optimisticUpdate)
|
|
|
|
if c.Store.NextSyncCommittee == nil {
|
|
c.Logger.Debug("checking for sync committee update")
|
|
currentPeriod := CalcSyncPeriod(uint64(c.Store.FinalizedHeader.Slot))
|
|
updates, err := c.API.GetUpdates(currentPeriod, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(updates) == 1 {
|
|
update := updates[0]
|
|
err = c.VerifyUpdate(update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.Logger.Info("updating sync committee")
|
|
c.ApplyUpdate(update)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) bootstrap() error {
|
|
forkedBootstrap, err := c.API.GetBootstrap(c.InitialCheckpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bootstrap, err := FromBootstrap(forkedBootstrap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
isValid := c.isValidCheckpoint(bootstrap.Header.Slot)
|
|
if !isValid {
|
|
if c.Config.StrictCheckpointAge {
|
|
return errors.New("checkpoint is too old")
|
|
} else {
|
|
c.Logger.Warn("checkpoint is too old")
|
|
}
|
|
}
|
|
|
|
committeeValid := c.isCurrentCommitteeProofValid(*bootstrap.Header, bootstrap.CurrentSyncCommittee, bootstrap.CurrentSyncCommitteeBranch)
|
|
|
|
headerHash := bootstrap.Header.HashTreeRoot(tree.GetHashFn()).String()
|
|
expectedHash := c.InitialCheckpoint.String()
|
|
|
|
headerValid := headerHash == expectedHash
|
|
|
|
if !headerValid {
|
|
return fmt.Errorf("header hash %s does not match expected hash %s", headerHash, expectedHash)
|
|
}
|
|
|
|
if !committeeValid {
|
|
return errors.New("committee proof is invalid")
|
|
}
|
|
|
|
c.Store = LightClientStore{
|
|
FinalizedHeader: bootstrap.Header,
|
|
CurrentSyncCommittee: &bootstrap.CurrentSyncCommittee,
|
|
OptimisticHeader: bootstrap.Header,
|
|
PreviousMaxActiveParticipants: view.Uint64View(0),
|
|
CurrentMaxActiveParticipants: view.Uint64View(0),
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) isValidCheckpoint(blockHashSlot common.Slot) bool {
|
|
currentSlot := c.expectedCurrentSlot()
|
|
currentSlotTimestamp, err := c.slotTimestamp(currentSlot)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
blockHashSlotTimestamp, err := c.slotTimestamp(blockHashSlot)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
slotAge := currentSlotTimestamp - blockHashSlotTimestamp
|
|
|
|
return uint64(slotAge) < c.Config.MaxCheckpointAge
|
|
}
|
|
|
|
func (c *ConsensusLightClient) VerifyGenericUpdate(update *GenericUpdate) error {
|
|
bits := c.getBits(update.SyncAggregate.SyncCommitteeBits)
|
|
if bits == 0 {
|
|
return ErrInsufficientParticipation
|
|
}
|
|
updateFinalizedSlot := common.Slot(0)
|
|
if update.FinalizedHeader != nil {
|
|
updateFinalizedSlot = update.FinalizedHeader.Slot
|
|
}
|
|
validTime := uint64(c.expectedCurrentSlot()) >= uint64(update.SignatureSlot) && update.SignatureSlot > update.AttestedHeader.Slot && update.AttestedHeader.Slot >= updateFinalizedSlot
|
|
if !validTime {
|
|
return ErrInvalidTimestamp
|
|
}
|
|
|
|
storePeriod := CalcSyncPeriod(uint64(c.Store.FinalizedHeader.Slot))
|
|
updateSigPeriod := CalcSyncPeriod(uint64(update.SignatureSlot))
|
|
validPeriod := false
|
|
if c.Store.NextSyncCommittee != nil {
|
|
validPeriod = updateSigPeriod == storePeriod || updateSigPeriod == storePeriod+1
|
|
} else {
|
|
validPeriod = updateSigPeriod == storePeriod
|
|
}
|
|
if !validPeriod {
|
|
return ErrInvalidPeriod
|
|
}
|
|
|
|
updateAttestedPeriod := CalcSyncPeriod(uint64(update.AttestedHeader.Slot))
|
|
updateHasNextCommittee := c.Store.NextSyncCommittee == nil && update.NextSyncCommittee != nil && updateAttestedPeriod == storePeriod
|
|
|
|
if update.AttestedHeader.Slot <= c.Store.FinalizedHeader.Slot && !updateHasNextCommittee {
|
|
return ErrNotRelevant
|
|
}
|
|
if update.FinalizedHeader != nil && update.FinalityBranch != nil {
|
|
isValid := IsFinalityProofValid(*update.AttestedHeader, *update.FinalizedHeader, *update.FinalityBranch)
|
|
if !isValid {
|
|
return ErrInvalidFinalityProof
|
|
}
|
|
}
|
|
if update.NextSyncCommittee != nil && update.NextSyncCommitteeBranch != nil {
|
|
isValid := IsNextCommitteeProofValid(*update.AttestedHeader, *update.NextSyncCommittee, *update.NextSyncCommitteeBranch)
|
|
if !isValid {
|
|
return ErrInvalidNextSyncCommitteeProof
|
|
}
|
|
}
|
|
var syncCommittee *common.SyncCommittee
|
|
|
|
if updateSigPeriod == storePeriod {
|
|
syncCommittee = c.Store.CurrentSyncCommittee
|
|
} else {
|
|
syncCommittee = c.Store.NextSyncCommittee
|
|
}
|
|
|
|
pks := c.getParticipatingKeys(*syncCommittee, update.SyncAggregate.SyncCommitteeBits)
|
|
|
|
isValidSig, err := c.VerifySyncCommitteeSignature(pks, *update.AttestedHeader, update.SyncAggregate.SyncCommitteeSignature, update.SignatureSlot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !isValidSig {
|
|
return ErrInvalidSignature
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) VerifyUpdate(update common.SpecObj) error {
|
|
genericUpdate, err := FromLightClientUpdate(update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.VerifyGenericUpdate(genericUpdate)
|
|
}
|
|
|
|
func (c *ConsensusLightClient) VerifyFinalityUpdate(update common.SpecObj) error {
|
|
genericUpdate, err := FromLightClientFinalityUpdate(update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.VerifyGenericUpdate(genericUpdate)
|
|
}
|
|
|
|
func (c *ConsensusLightClient) VerifyOptimisticUpdate(update common.SpecObj) error {
|
|
genericUpdate, err := FromLightClientOptimisticUpdate(update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.VerifyGenericUpdate(genericUpdate)
|
|
}
|
|
|
|
func (c *ConsensusLightClient) ApplyGenericUpdate(update *GenericUpdate) {
|
|
commiteeBits := c.getBits(update.SyncAggregate.SyncCommitteeBits)
|
|
|
|
if c.Store.CurrentMaxActiveParticipants < view.Uint64View(commiteeBits) {
|
|
c.Store.CurrentMaxActiveParticipants = view.Uint64View(commiteeBits)
|
|
}
|
|
|
|
shouldUpdateOptimistic := commiteeBits > c.safetyThreshold() && update.AttestedHeader.Slot > c.Store.OptimisticHeader.Slot
|
|
|
|
if shouldUpdateOptimistic {
|
|
c.Store.OptimisticHeader = update.AttestedHeader
|
|
c.logFinalityUpdate(update)
|
|
}
|
|
|
|
updateAttestedPeriod := CalcSyncPeriod(uint64(update.AttestedHeader.Slot))
|
|
|
|
updateFinalizedSlot := common.Slot(0)
|
|
if update.FinalizedHeader != nil {
|
|
updateFinalizedSlot = update.FinalizedHeader.Slot
|
|
}
|
|
|
|
updateFinalizedPeriod := CalcSyncPeriod(uint64(updateFinalizedSlot))
|
|
|
|
updateHasFinalizedNextCommittee := c.Store.NextSyncCommittee == nil &&
|
|
c.hasSyncUpdate(update) &&
|
|
c.hasFinalityUpdate(update) &&
|
|
updateFinalizedPeriod == updateAttestedPeriod
|
|
|
|
hasMajority := commiteeBits*3 >= 512*2
|
|
updateIsNewer := updateFinalizedSlot > c.Store.FinalizedHeader.Slot
|
|
goodUpdate := updateIsNewer || updateHasFinalizedNextCommittee
|
|
|
|
shouldApplyUpdate := hasMajority && goodUpdate
|
|
|
|
if shouldApplyUpdate {
|
|
storePeriod := CalcSyncPeriod(uint64(c.Store.FinalizedHeader.Slot))
|
|
|
|
if c.Store.NextSyncCommittee == nil {
|
|
c.Store.NextSyncCommittee = update.NextSyncCommittee
|
|
} else if updateFinalizedPeriod == storePeriod+1 {
|
|
c.Logger.Info("sync committee updated")
|
|
c.Store.CurrentSyncCommittee = c.Store.NextSyncCommittee
|
|
c.Store.NextSyncCommittee = update.NextSyncCommittee
|
|
c.Store.PreviousMaxActiveParticipants = c.Store.CurrentMaxActiveParticipants
|
|
c.Store.CurrentMaxActiveParticipants = 0
|
|
}
|
|
|
|
if updateFinalizedSlot > c.Store.FinalizedHeader.Slot {
|
|
c.Store.FinalizedHeader = update.FinalizedHeader
|
|
c.logFinalityUpdate(update)
|
|
|
|
if c.Store.FinalizedHeader.Slot%32 == 0 {
|
|
checkpoint := c.Store.FinalizedHeader.HashTreeRoot(tree.GetHashFn())
|
|
c.LastCheckpoint = checkpoint
|
|
}
|
|
|
|
if c.Store.FinalizedHeader.Slot > c.Store.OptimisticHeader.Slot {
|
|
c.Store.OptimisticHeader = c.Store.FinalizedHeader
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *ConsensusLightClient) ApplyUpdate(update common.SpecObj) error {
|
|
genericUpdate, err := FromLightClientUpdate(update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.ApplyGenericUpdate(genericUpdate)
|
|
return nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) ApplyFinalityUpdate(update common.SpecObj) error {
|
|
genericUpdate, err := FromLightClientFinalityUpdate(update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.ApplyGenericUpdate(genericUpdate)
|
|
return nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) ApplyOptimisticUpdate(update common.SpecObj) error {
|
|
genericUpdate, err := FromLightClientOptimisticUpdate(update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.ApplyGenericUpdate(genericUpdate)
|
|
return nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) VerifySyncCommitteeSignature(pks []common.BLSPubkey, attestedHeader common.BeaconBlockHeader, signature common.BLSSignature, signatureSlot common.Slot) (bool, error) {
|
|
headerRoot := attestedHeader.HashTreeRoot(tree.GetHashFn())
|
|
signingRoot := c.ComputeCommitteeSignRoot(headerRoot, signatureSlot)
|
|
blsuPubKeys := make([]*blsu.Pubkey, 0, len(pks))
|
|
for _, p := range pks {
|
|
blsuPubKey, err := p.Pubkey()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
blsuPubKeys = append(blsuPubKeys, blsuPubKey)
|
|
}
|
|
blsuSig, err := signature.Signature()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return blsu.FastAggregateVerify(blsuPubKeys, signingRoot[:], blsuSig), nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) ComputeCommitteeSignRoot(headerRoot tree.Root, slot common.Slot) common.Root {
|
|
genesisRoot := c.Config.Chain.GenesisRoot
|
|
domainType := hexutil.MustDecode("0x07000000")
|
|
forkVersion := c.Config.Spec.ForkVersion(slot)
|
|
domain := common.ComputeDomain(common.BLSDomainType(domainType), forkVersion, genesisRoot)
|
|
return ComputeSigningRoot(headerRoot, domain)
|
|
}
|
|
|
|
func (c *ConsensusLightClient) expectedCurrentSlot() common.Slot {
|
|
return c.Config.Spec.TimeToSlot(common.Timestamp(time.Now().Unix()), common.Timestamp(c.Config.Chain.GenesisTime))
|
|
}
|
|
|
|
func (c *ConsensusLightClient) slotTimestamp(slot common.Slot) (common.Timestamp, error) {
|
|
atSlot, err := c.Config.Spec.TimeAtSlot(slot, common.Timestamp(c.Config.Chain.GenesisTime))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return atSlot, nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) isCurrentCommitteeProofValid(attestedHeader common.BeaconBlockHeader, currentCommittee common.SyncCommittee, currentCommitteeBranch altair.SyncCommitteeProofBranch) bool {
|
|
return merkle.VerifyMerkleBranch(currentCommittee.HashTreeRoot(c.Config.Spec, tree.GetHashFn()), currentCommitteeBranch[:], 5, 22, attestedHeader.StateRoot)
|
|
}
|
|
|
|
func (c *ConsensusLightClient) safetyThreshold() uint64 {
|
|
if c.Store.CurrentMaxActiveParticipants > c.Store.PreviousMaxActiveParticipants {
|
|
return uint64(c.Store.CurrentMaxActiveParticipants) / 2
|
|
} else {
|
|
return uint64(c.Store.PreviousMaxActiveParticipants) / 2
|
|
}
|
|
}
|
|
|
|
func (c *ConsensusLightClient) hasSyncUpdate(update *GenericUpdate) bool {
|
|
return update.NextSyncCommittee != nil && update.NextSyncCommitteeBranch != nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) hasFinalityUpdate(update *GenericUpdate) bool {
|
|
return update.FinalizedHeader != nil && update.FinalityBranch != nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) logFinalityUpdate(update *GenericUpdate) {
|
|
count := c.getBits(update.SyncAggregate.SyncCommitteeBits)
|
|
participation := float32(count) / 512 * 100
|
|
decimals := 0
|
|
if participation == 100.0 {
|
|
decimals = 1
|
|
} else {
|
|
decimals = 2
|
|
}
|
|
slot := c.Store.OptimisticHeader.Slot
|
|
age, err := c.age(slot)
|
|
if err != nil {
|
|
c.Logger.Error("failed to get age", "slot is", slot, "err is", err)
|
|
return
|
|
}
|
|
days := int(age.Hours() / 24)
|
|
hours := int(age.Hours()) % 24
|
|
minutes := int(age.Minutes()) % 60
|
|
secs := int(age.Seconds()) % 60
|
|
ageStr := fmt.Sprintf("%d:%d:%d:%d", days, hours, minutes, secs)
|
|
c.Logger.Info("update header", "slot=", slot, "confidence=", decimals, "age", ageStr)
|
|
}
|
|
|
|
func (c *ConsensusLightClient) age(slot common.Slot) (time.Duration, error) {
|
|
expectTime, err := c.slotTimestamp(slot)
|
|
if err != nil {
|
|
return time.Duration(0), err
|
|
}
|
|
return time.Since(time.Unix(int64(expectTime), 0)), nil
|
|
}
|
|
|
|
func (c *ConsensusLightClient) getBits(sync altair.SyncCommitteeBits) uint64 {
|
|
res := 0
|
|
for i := 0; i < int(c.Config.Spec.SYNC_COMMITTEE_SIZE); i++ {
|
|
if sync.GetBit(uint64(i)) {
|
|
res++
|
|
}
|
|
}
|
|
return uint64(res)
|
|
}
|
|
|
|
func (c *ConsensusLightClient) getParticipatingKeys(committee common.SyncCommittee, syncBits altair.SyncCommitteeBits) []common.BLSPubkey {
|
|
res := make([]common.BLSPubkey, 0)
|
|
for i := 0; i < int(c.Config.Spec.SYNC_COMMITTEE_SIZE); i++ {
|
|
if syncBits.GetBit(uint64(i)) {
|
|
res = append(res, committee.Pubkeys[i])
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func FromLightClientUpdate(commonUpdate common.SpecObj) (*GenericUpdate, error) {
|
|
switch update := commonUpdate.(type) {
|
|
case *deneb.LightClientUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
NextSyncCommittee: &update.NextSyncCommittee,
|
|
NextSyncCommitteeBranch: &update.NextSyncCommitteeBranch,
|
|
FinalizedHeader: &update.FinalizedHeader.Beacon,
|
|
FinalityBranch: &update.FinalityBranch,
|
|
}, nil
|
|
case *capella.LightClientUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
NextSyncCommittee: &update.NextSyncCommittee,
|
|
NextSyncCommitteeBranch: &update.NextSyncCommitteeBranch,
|
|
FinalizedHeader: &update.FinalizedHeader.Beacon,
|
|
FinalityBranch: &update.FinalityBranch,
|
|
}, nil
|
|
case *altair.LightClientUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
NextSyncCommittee: &update.NextSyncCommittee,
|
|
NextSyncCommitteeBranch: &update.NextSyncCommitteeBranch,
|
|
FinalizedHeader: &update.FinalizedHeader.Beacon,
|
|
FinalityBranch: &update.FinalityBranch,
|
|
}, nil
|
|
}
|
|
return nil, errors.New("unknown update type")
|
|
}
|
|
|
|
func FromLightClientFinalityUpdate(commonFinalityUpdate common.SpecObj) (*GenericUpdate, error) {
|
|
switch update := commonFinalityUpdate.(type) {
|
|
case *deneb.LightClientFinalityUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
FinalizedHeader: &update.FinalizedHeader.Beacon,
|
|
FinalityBranch: &update.FinalityBranch,
|
|
}, nil
|
|
case *capella.LightClientFinalityUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
FinalizedHeader: &update.FinalizedHeader.Beacon,
|
|
FinalityBranch: &update.FinalityBranch,
|
|
}, nil
|
|
case *altair.LightClientFinalityUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
FinalizedHeader: &update.FinalizedHeader,
|
|
FinalityBranch: &update.FinalityBranch,
|
|
}, nil
|
|
}
|
|
return nil, errors.New("unknown finality update type")
|
|
}
|
|
|
|
func FromLightClientOptimisticUpdate(commonOptimisticUpdate common.SpecObj) (*GenericUpdate, error) {
|
|
switch update := commonOptimisticUpdate.(type) {
|
|
case *deneb.LightClientOptimisticUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
}, nil
|
|
case *capella.LightClientOptimisticUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
}, nil
|
|
case *altair.LightClientOptimisticUpdate:
|
|
return &GenericUpdate{
|
|
AttestedHeader: &update.AttestedHeader.Beacon,
|
|
SyncAggregate: &update.SyncAggregate,
|
|
SignatureSlot: update.SignatureSlot,
|
|
}, nil
|
|
}
|
|
return nil, errors.New("unknown optimistic update type")
|
|
}
|
|
|
|
func ComputeSigningRoot(root common.Root, domain common.BLSDomain) common.Root {
|
|
data := common.SigningData{
|
|
ObjectRoot: root,
|
|
Domain: domain,
|
|
}
|
|
return data.HashTreeRoot(tree.GetHashFn())
|
|
}
|
|
|
|
func CalcSyncPeriod(slot uint64) uint64 {
|
|
epoch := slot / 32 // 32 slots per epoch
|
|
return epoch / 256 // 256 epochs per sync committee
|
|
}
|
|
|
|
func IsFinalityProofValid(attestedHeader common.BeaconBlockHeader, finalityHeader common.BeaconBlockHeader, finalityBranch altair.FinalizedRootProofBranch) bool {
|
|
leaf := finalityHeader.HashTreeRoot(tree.GetHashFn())
|
|
root := attestedHeader.StateRoot
|
|
return merkle.VerifyMerkleBranch(leaf, finalityBranch[:], 6, 41, root)
|
|
}
|
|
|
|
func IsNextCommitteeProofValid(attestedHeader common.BeaconBlockHeader, nextCommittee common.SyncCommittee, nextCommitteeBranch altair.SyncCommitteeProofBranch) bool {
|
|
leaf := nextCommittee.HashTreeRoot(configs.Mainnet, tree.GetHashFn())
|
|
root := attestedHeader.StateRoot
|
|
return merkle.VerifyMerkleBranch(leaf, nextCommitteeBranch[:], 5, 23, root)
|
|
}
|