2019-01-19 02:57:51 +00:00
|
|
|
package blockchain
|
|
|
|
|
|
|
|
import (
|
2019-03-13 21:17:32 +00:00
|
|
|
"context"
|
2019-01-19 02:57:51 +00:00
|
|
|
"fmt"
|
|
|
|
|
2019-03-25 15:21:21 +00:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
2019-04-19 03:26:22 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
2019-02-13 23:13:00 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
2019-01-19 02:57:51 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
2019-01-31 02:53:58 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
2019-04-19 03:26:22 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
2019-03-03 17:31:29 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
2019-03-25 15:21:21 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2019-03-13 21:17:32 +00:00
|
|
|
"go.opencensus.io/trace"
|
2019-01-19 02:57:51 +00:00
|
|
|
)
|
|
|
|
|
2019-03-25 15:21:21 +00:00
|
|
|
var (
|
|
|
|
reorgCount = promauto.NewCounter(prometheus.CounterOpts{
|
|
|
|
Name: "reorg_counter",
|
|
|
|
Help: "The number of chain reorganization events that have happened in the fork choice rule",
|
|
|
|
})
|
|
|
|
)
|
2019-04-19 03:26:22 +00:00
|
|
|
var blkAncestorCache = cache.NewBlockAncestorCache()
|
2019-03-25 15:21:21 +00:00
|
|
|
|
2019-03-19 23:07:49 +00:00
|
|
|
// ForkChoice interface defines the methods for applying fork choice rule
|
|
|
|
// operations to the blockchain.
|
|
|
|
type ForkChoice interface {
|
|
|
|
ApplyForkChoiceRule(ctx context.Context, block *pb.BeaconBlock, computedState *pb.BeaconState) error
|
|
|
|
}
|
|
|
|
|
2019-03-19 17:16:17 +00:00
|
|
|
// updateFFGCheckPts checks whether the existing FFG check points saved in DB
|
|
|
|
// are not older than the ones just processed in state. If it's older, we update
|
|
|
|
// the db with the latest FFG check points, both justification and finalization.
|
2019-04-04 13:30:23 +00:00
|
|
|
func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconState) error {
|
2019-03-19 17:16:17 +00:00
|
|
|
lastJustifiedSlot := helpers.StartSlot(state.JustifiedEpoch)
|
|
|
|
savedJustifiedBlock, err := c.beaconDB.JustifiedBlock()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// If the last processed justification slot in state is greater than
|
|
|
|
// the slot of justified block saved in DB.
|
|
|
|
if lastJustifiedSlot > savedJustifiedBlock.Slot {
|
|
|
|
// Retrieve the new justified block from DB using the new justified slot and save it.
|
2019-04-06 00:19:27 +00:00
|
|
|
newJustifiedBlock, err := c.beaconDB.BlockBySlot(ctx, lastJustifiedSlot)
|
2019-03-19 17:16:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-20 17:18:55 +00:00
|
|
|
// If the new justified slot is a skip slot in db then we keep getting it's ancestors
|
|
|
|
// until we can get a block.
|
2019-03-21 01:57:34 +00:00
|
|
|
lastAvailBlkSlot := lastJustifiedSlot
|
2019-03-20 17:18:55 +00:00
|
|
|
for newJustifiedBlock == nil {
|
|
|
|
log.Debugf("Saving new justified block, no block with slot %d in db, trying slot %d",
|
2019-03-21 01:57:34 +00:00
|
|
|
lastAvailBlkSlot, lastAvailBlkSlot-1)
|
|
|
|
lastAvailBlkSlot--
|
2019-04-06 00:19:27 +00:00
|
|
|
newJustifiedBlock, err = c.beaconDB.BlockBySlot(ctx, lastAvailBlkSlot)
|
2019-03-20 17:18:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-04-05 03:39:51 +00:00
|
|
|
|
|
|
|
// Fetch justified state from historical states db.
|
|
|
|
newJustifiedState, err := c.beaconDB.HistoricalStateFromSlot(ctx, newJustifiedBlock.Slot)
|
2019-03-19 17:16:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-20 22:33:31 +00:00
|
|
|
if err := c.beaconDB.SaveJustifiedBlock(newJustifiedBlock); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-19 17:16:17 +00:00
|
|
|
if err := c.beaconDB.SaveJustifiedState(newJustifiedState); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lastFinalizedSlot := helpers.StartSlot(state.FinalizedEpoch)
|
|
|
|
savedFinalizedBlock, err := c.beaconDB.FinalizedBlock()
|
|
|
|
// If the last processed finalized slot in state is greater than
|
|
|
|
// the slot of finalized block saved in DB.
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if lastFinalizedSlot > savedFinalizedBlock.Slot {
|
|
|
|
// Retrieve the new finalized block from DB using the new finalized slot and save it.
|
2019-04-06 00:19:27 +00:00
|
|
|
newFinalizedBlock, err := c.beaconDB.BlockBySlot(ctx, lastFinalizedSlot)
|
2019-03-19 17:16:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-20 17:18:55 +00:00
|
|
|
// If the new finalized slot is a skip slot in db then we keep getting it's ancestors
|
|
|
|
// until we can get a block.
|
2019-03-21 01:57:34 +00:00
|
|
|
lastAvailBlkSlot := lastFinalizedSlot
|
2019-03-20 17:18:55 +00:00
|
|
|
for newFinalizedBlock == nil {
|
|
|
|
log.Debugf("Saving new finalized block, no block with slot %d in db, trying slot %d",
|
2019-03-21 01:57:34 +00:00
|
|
|
lastAvailBlkSlot, lastAvailBlkSlot-1)
|
|
|
|
lastAvailBlkSlot--
|
2019-04-06 00:19:27 +00:00
|
|
|
newFinalizedBlock, err = c.beaconDB.BlockBySlot(ctx, lastAvailBlkSlot)
|
2019-03-20 17:18:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-03-20 22:33:31 +00:00
|
|
|
|
2019-04-05 03:39:51 +00:00
|
|
|
// Generate the new finalized state with using new finalized block and
|
|
|
|
// save it.
|
|
|
|
newFinalizedState, err := c.beaconDB.HistoricalStateFromSlot(ctx, lastFinalizedSlot)
|
2019-03-19 17:16:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-20 22:33:31 +00:00
|
|
|
if err := c.beaconDB.SaveFinalizedBlock(newFinalizedBlock); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-19 17:16:17 +00:00
|
|
|
if err := c.beaconDB.SaveFinalizedState(newFinalizedState); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-03 15:32:59 +00:00
|
|
|
// ApplyForkChoiceRule determines the current beacon chain head using LMD
|
|
|
|
// GHOST as a block-vote weighted function to select a canonical head in
|
|
|
|
// Ethereum Serenity. The inputs are the the recently processed block and its
|
|
|
|
// associated state.
|
|
|
|
func (c *ChainService) ApplyForkChoiceRule(
|
|
|
|
ctx context.Context,
|
|
|
|
block *pb.BeaconBlock,
|
|
|
|
postState *pb.BeaconState,
|
|
|
|
) error {
|
2019-03-13 21:17:32 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ApplyForkChoiceRule")
|
|
|
|
defer span.End()
|
2019-03-25 15:21:21 +00:00
|
|
|
log.Info("Applying LMD-GHOST Fork Choice Rule")
|
|
|
|
|
2019-03-27 19:53:43 +00:00
|
|
|
justifiedState, err := c.beaconDB.JustifiedState()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not retrieve justified state: %v", err)
|
|
|
|
}
|
2019-04-04 13:30:23 +00:00
|
|
|
attestationTargets, err := c.attestationTargets(ctx, justifiedState)
|
2019-03-13 21:17:32 +00:00
|
|
|
if err != nil {
|
2019-03-25 15:21:21 +00:00
|
|
|
return fmt.Errorf("could not retrieve attestation target: %v", err)
|
2019-03-13 21:17:32 +00:00
|
|
|
}
|
2019-03-25 15:21:21 +00:00
|
|
|
justifiedHead, err := c.beaconDB.JustifiedBlock()
|
|
|
|
if err != nil {
|
2019-04-03 15:13:19 +00:00
|
|
|
return fmt.Errorf("could not retrieve justified head: %v", err)
|
2019-03-25 15:21:21 +00:00
|
|
|
}
|
2019-04-06 00:19:27 +00:00
|
|
|
head, err := c.lmdGhost(ctx, justifiedHead, justifiedState, attestationTargets)
|
2019-03-25 15:21:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not run fork choice: %v", err)
|
2019-03-13 21:17:32 +00:00
|
|
|
}
|
2019-04-16 18:19:31 +00:00
|
|
|
headRoot, err := hashutil.HashBeaconBlock(head)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not hash head block: %v", err)
|
|
|
|
}
|
|
|
|
c.canonicalBlocksLock.Lock()
|
|
|
|
defer c.canonicalBlocksLock.Unlock()
|
|
|
|
c.canonicalBlocks[head.Slot] = headRoot[:]
|
|
|
|
|
2019-03-25 15:21:21 +00:00
|
|
|
newState := postState
|
|
|
|
if head.Slot != block.Slot {
|
|
|
|
log.Warnf("Reorg happened, last processed block at slot %d, new head block at slot %d",
|
|
|
|
block.Slot-params.BeaconConfig().GenesisSlot, head.Slot-params.BeaconConfig().GenesisSlot)
|
2019-03-19 17:16:17 +00:00
|
|
|
|
2019-03-25 15:21:21 +00:00
|
|
|
// Only regenerate head state if there was a reorg.
|
2019-04-05 03:39:51 +00:00
|
|
|
newState, err = c.beaconDB.HistoricalStateFromSlot(ctx, head.Slot)
|
2019-03-25 15:21:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not gen state: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if newState.Slot != postState.Slot {
|
|
|
|
log.Warnf("Reorg happened, post state slot at %d, new head state at slot %d",
|
|
|
|
postState.Slot-params.BeaconConfig().GenesisSlot, newState.Slot-params.BeaconConfig().GenesisSlot)
|
|
|
|
}
|
|
|
|
|
2019-04-16 18:19:31 +00:00
|
|
|
for revertedSlot := block.Slot; revertedSlot > head.Slot; revertedSlot-- {
|
|
|
|
delete(c.canonicalBlocks, revertedSlot)
|
|
|
|
}
|
2019-03-25 15:21:21 +00:00
|
|
|
reorgCount.Inc()
|
2019-03-14 23:05:31 +00:00
|
|
|
}
|
2019-03-17 02:56:05 +00:00
|
|
|
|
2019-04-02 08:49:45 +00:00
|
|
|
if err := c.beaconDB.UpdateChainHead(ctx, head, newState); err != nil {
|
2019-03-25 15:21:21 +00:00
|
|
|
return fmt.Errorf("failed to update chain: %v", err)
|
|
|
|
}
|
|
|
|
h, err := hashutil.HashBeaconBlock(head)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not hash head: %v", err)
|
|
|
|
}
|
|
|
|
log.WithField("headRoot", fmt.Sprintf("0x%x", h)).Info("Chain head block and state updated")
|
2019-03-13 21:17:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// lmdGhost applies the Latest Message Driven, Greediest Heaviest Observed Sub-Tree
|
|
|
|
// fork-choice rule defined in the Ethereum Serenity specification for the beacon chain.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock) -> BeaconBlock:
|
|
|
|
// """
|
|
|
|
// Execute the LMD-GHOST algorithm to find the head ``BeaconBlock``.
|
|
|
|
// """
|
|
|
|
// validators = start_state.validator_registry
|
|
|
|
// active_validator_indices = get_active_validator_indices(validators, slot_to_epoch(start_state.slot))
|
|
|
|
// attestation_targets = [
|
|
|
|
// (validator_index, get_latest_attestation_target(store, validator_index))
|
|
|
|
// for validator_index in active_validator_indices
|
|
|
|
// ]
|
|
|
|
//
|
|
|
|
// def get_vote_count(block: BeaconBlock) -> int:
|
|
|
|
// return sum(
|
|
|
|
// get_effective_balance(start_state.validator_balances[validator_index]) // FORK_CHOICE_BALANCE_INCREMENT
|
|
|
|
// for validator_index, target in attestation_targets
|
|
|
|
// if get_ancestor(store, target, block.slot) == block
|
|
|
|
// )
|
|
|
|
//
|
|
|
|
// head = start_block
|
|
|
|
// while 1:
|
|
|
|
// children = get_children(store, head)
|
|
|
|
// if len(children) == 0:
|
|
|
|
// return head
|
|
|
|
// head = max(children, key=get_vote_count)
|
|
|
|
func (c *ChainService) lmdGhost(
|
2019-04-06 00:19:27 +00:00
|
|
|
ctx context.Context,
|
2019-03-27 19:53:43 +00:00
|
|
|
startBlock *pb.BeaconBlock,
|
|
|
|
startState *pb.BeaconState,
|
2019-03-13 21:17:32 +00:00
|
|
|
voteTargets map[uint64]*pb.BeaconBlock,
|
|
|
|
) (*pb.BeaconBlock, error) {
|
2019-04-03 15:32:59 +00:00
|
|
|
highestSlot := c.beaconDB.HighestBlockSlot()
|
|
|
|
|
2019-03-27 19:53:43 +00:00
|
|
|
head := startBlock
|
2019-03-13 21:17:32 +00:00
|
|
|
for {
|
2019-04-06 00:19:27 +00:00
|
|
|
children, err := c.blockChildren(ctx, head, highestSlot)
|
2019-03-13 21:17:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not fetch block children: %v", err)
|
|
|
|
}
|
|
|
|
if len(children) == 0 {
|
|
|
|
return head, nil
|
|
|
|
}
|
|
|
|
maxChild := children[0]
|
|
|
|
|
2019-03-27 19:53:43 +00:00
|
|
|
maxChildVotes, err := VoteCount(maxChild, startState, voteTargets, c.beaconDB)
|
2019-03-13 21:17:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to determine vote count for block: %v", err)
|
|
|
|
}
|
|
|
|
for i := 0; i < len(children); i++ {
|
2019-03-27 19:53:43 +00:00
|
|
|
candidateChildVotes, err := VoteCount(children[i], startState, voteTargets, c.beaconDB)
|
2019-03-13 21:17:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to determine vote count for block: %v", err)
|
|
|
|
}
|
|
|
|
if candidateChildVotes > maxChildVotes {
|
|
|
|
maxChild = children[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
head = maxChild
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-03 15:32:59 +00:00
|
|
|
// blockChildren returns the child blocks of the given block up to a given
|
|
|
|
// highest slot.
|
|
|
|
//
|
2019-03-13 21:17:32 +00:00
|
|
|
// ex:
|
|
|
|
// /- C - E
|
|
|
|
// A - B - D - F
|
|
|
|
// \- G
|
|
|
|
// Input: B. Output: [C, D, G]
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// get_children(store: Store, block: BeaconBlock) -> List[BeaconBlock]
|
|
|
|
// returns the child blocks of the given block.
|
2019-04-06 00:19:27 +00:00
|
|
|
func (c *ChainService) blockChildren(ctx context.Context, block *pb.BeaconBlock, highestSlot uint64) ([]*pb.BeaconBlock, error) {
|
2019-03-13 21:17:32 +00:00
|
|
|
var children []*pb.BeaconBlock
|
|
|
|
|
|
|
|
currentRoot, err := hashutil.HashBeaconBlock(block)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not tree hash incoming block: %v", err)
|
|
|
|
}
|
|
|
|
startSlot := block.Slot + 1
|
2019-04-03 15:32:59 +00:00
|
|
|
for i := startSlot; i <= highestSlot; i++ {
|
2019-04-06 00:19:27 +00:00
|
|
|
block, err := c.beaconDB.BlockBySlot(ctx, i)
|
2019-03-13 21:17:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get block by slot: %v", err)
|
|
|
|
}
|
|
|
|
// Continue if there's a skip block.
|
|
|
|
if block == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
|
|
|
|
if currentRoot == parentRoot {
|
|
|
|
children = append(children, block)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return children, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// attestationTargets retrieves the list of attestation targets since last finalized epoch,
|
|
|
|
// each attestation target consists of validator index and its attestation target (i.e. the block
|
|
|
|
// which the validator attested to)
|
2019-04-04 13:30:23 +00:00
|
|
|
func (c *ChainService) attestationTargets(ctx context.Context, state *pb.BeaconState) (map[uint64]*pb.BeaconBlock, error) {
|
2019-03-27 19:53:43 +00:00
|
|
|
indices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
2019-03-25 15:21:21 +00:00
|
|
|
attestationTargets := make(map[uint64]*pb.BeaconBlock)
|
2019-03-13 21:17:32 +00:00
|
|
|
for i, index := range indices {
|
2019-04-04 13:30:23 +00:00
|
|
|
block, err := c.attsService.LatestAttestationTarget(ctx, index)
|
2019-03-13 21:17:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not retrieve attestation target: %v", err)
|
|
|
|
}
|
2019-03-25 15:21:21 +00:00
|
|
|
if block == nil {
|
|
|
|
continue
|
2019-03-13 21:17:32 +00:00
|
|
|
}
|
2019-03-25 15:21:21 +00:00
|
|
|
attestationTargets[uint64(i)] = block
|
2019-03-13 21:17:32 +00:00
|
|
|
}
|
|
|
|
return attestationTargets, nil
|
|
|
|
}
|
|
|
|
|
2019-01-19 02:57:51 +00:00
|
|
|
// VoteCount determines the number of votes on a beacon block by counting the number
|
|
|
|
// of target blocks that have such beacon block as a common ancestor.
|
2019-01-29 11:19:17 +00:00
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
2019-02-13 23:13:00 +00:00
|
|
|
// def get_vote_count(block: BeaconBlock) -> int:
|
|
|
|
// return sum(
|
|
|
|
// get_effective_balance(start_state.validator_balances[validator_index]) // FORK_CHOICE_BALANCE_INCREMENT
|
|
|
|
// for validator_index, target in attestation_targets
|
|
|
|
// if get_ancestor(store, target, block.slot) == block
|
|
|
|
// )
|
|
|
|
func VoteCount(block *pb.BeaconBlock, state *pb.BeaconState, targets map[uint64]*pb.BeaconBlock, beaconDB *db.BeaconDB) (int, error) {
|
|
|
|
balances := 0
|
2019-04-19 03:26:22 +00:00
|
|
|
var ancestor *pb.BeaconBlock
|
|
|
|
var err error
|
|
|
|
|
2019-02-13 23:13:00 +00:00
|
|
|
for validatorIndex, targetBlock := range targets {
|
2019-04-19 03:26:22 +00:00
|
|
|
if featureconfig.FeatureConfig().EnableBlockAncestorCache {
|
|
|
|
ancestor, err = cachedAncestorBlock(targetBlock, block.Slot, beaconDB)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// if block ancestor cache was not enabled, retrieve the ancestor recursively.
|
|
|
|
ancestor, err = BlockAncestor(targetBlock, block.Slot, beaconDB)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2019-01-19 02:57:51 +00:00
|
|
|
}
|
2019-03-08 13:55:55 +00:00
|
|
|
// This covers the following case, we start at B5, and want to process B6 and B7
|
|
|
|
// B6 can be processed, B7 can not be processed because it's pointed to the
|
|
|
|
// block older than current block 5.
|
|
|
|
// B4 - B5 - B6
|
|
|
|
// \ - - - - - B7
|
|
|
|
if ancestor == nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-02-26 03:42:31 +00:00
|
|
|
ancestorRoot, err := hashutil.HashBeaconBlock(ancestor)
|
2019-01-19 02:57:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2019-02-26 03:42:31 +00:00
|
|
|
blockRoot, err := hashutil.HashBeaconBlock(block)
|
2019-01-19 02:57:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2019-02-14 20:04:47 +00:00
|
|
|
if blockRoot == ancestorRoot {
|
2019-02-13 23:13:00 +00:00
|
|
|
balances += int(helpers.EffectiveBalance(state, validatorIndex))
|
2019-01-19 02:57:51 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-13 23:13:00 +00:00
|
|
|
return balances, nil
|
2019-01-19 02:57:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BlockAncestor obtains the ancestor at of a block at a certain slot.
|
2019-01-29 11:19:17 +00:00
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
2019-03-08 13:55:55 +00:00
|
|
|
// def get_ancestor(store: Store, block: BeaconBlock, slot: Slot) -> BeaconBlock:
|
|
|
|
// """
|
|
|
|
// Get the ancestor of ``block`` with slot number ``slot``; return ``None`` if not found.
|
|
|
|
// """
|
|
|
|
// if block.slot == slot:
|
|
|
|
// return block
|
|
|
|
// elif block.slot < slot:
|
|
|
|
// return None
|
|
|
|
// else:
|
|
|
|
// return get_ancestor(store, store.get_parent(block), slot)
|
2019-01-19 02:57:51 +00:00
|
|
|
func BlockAncestor(block *pb.BeaconBlock, slot uint64, beaconDB *db.BeaconDB) (*pb.BeaconBlock, error) {
|
|
|
|
if block.Slot == slot {
|
|
|
|
return block, nil
|
|
|
|
}
|
2019-03-08 13:55:55 +00:00
|
|
|
if block.Slot < slot {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2019-01-19 02:57:51 +00:00
|
|
|
parentHash := bytesutil.ToBytes32(block.ParentRootHash32)
|
2019-01-21 09:34:11 +00:00
|
|
|
parent, err := beaconDB.Block(parentHash)
|
2019-01-19 02:57:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get parent block: %v", err)
|
|
|
|
}
|
|
|
|
if parent == nil {
|
|
|
|
return nil, fmt.Errorf("parent block does not exist: %v", err)
|
|
|
|
}
|
|
|
|
return BlockAncestor(parent, slot, beaconDB)
|
|
|
|
}
|
2019-04-19 03:26:22 +00:00
|
|
|
|
|
|
|
// cachedAncestorBlock retrieves the cached ancestor block from block ancestor cache,
|
|
|
|
// if it's not there it looks up the block tree get it and cache it.
|
|
|
|
func cachedAncestorBlock(targetBlk *pb.BeaconBlock, height uint64, beaconDB *db.BeaconDB) (*pb.BeaconBlock, error) {
|
|
|
|
var ancestor *pb.BeaconBlock
|
|
|
|
|
|
|
|
// check if the ancestor block of from a given block height was cached.
|
|
|
|
targetHash, err := hashutil.HashBeaconBlock(targetBlk)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cachedAncestorBlock, err := blkAncestorCache.AncestorBySlot(targetHash[:], height)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
if cachedAncestorBlock != nil {
|
|
|
|
return cachedAncestorBlock.Block, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the ancestor to the cache if it was not cached.
|
|
|
|
ancestor, err = BlockAncestor(targetBlk, height, beaconDB)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := blkAncestorCache.AddBlockAncestor(&cache.AncestorInfo{
|
|
|
|
Hash: targetHash[:],
|
|
|
|
Height: height,
|
|
|
|
Block: ancestor,
|
|
|
|
}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ancestor, nil
|
|
|
|
}
|