github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/forkchoice/protoarray/store.go (about)

     1  package protoarray
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  
     8  	"github.com/pkg/errors"
     9  	types "github.com/prysmaticlabs/eth2-types"
    10  	"github.com/prysmaticlabs/prysm/shared/params"
    11  	"go.opencensus.io/trace"
    12  )
    13  
    14  // This defines the minimal number of block nodes that can be in the tree
    15  // before getting pruned upon new finalization.
    16  const defaultPruneThreshold = 256
    17  
    18  // This tracks the last reported head root. Used for metrics.
    19  var lastHeadRoot [32]byte
    20  
    21  // New initializes a new fork choice store.
    22  func New(justifiedEpoch, finalizedEpoch types.Epoch, finalizedRoot [32]byte) *ForkChoice {
    23  	s := &Store{
    24  		justifiedEpoch: justifiedEpoch,
    25  		finalizedEpoch: finalizedEpoch,
    26  		finalizedRoot:  finalizedRoot,
    27  		nodes:          make([]*Node, 0),
    28  		nodesIndices:   make(map[[32]byte]uint64),
    29  		canonicalNodes: make(map[[32]byte]bool),
    30  		pruneThreshold: defaultPruneThreshold,
    31  	}
    32  
    33  	b := make([]uint64, 0)
    34  	v := make([]Vote, 0)
    35  
    36  	return &ForkChoice{store: s, balances: b, votes: v}
    37  }
    38  
    39  // Head returns the head root from fork choice store.
    40  // It firsts computes validator's balance changes then recalculates block tree from leaves to root.
    41  func (f *ForkChoice) Head(
    42  	ctx context.Context,
    43  	justifiedEpoch types.Epoch,
    44  	justifiedRoot [32]byte,
    45  	justifiedStateBalances []uint64,
    46  	finalizedEpoch types.Epoch,
    47  ) ([32]byte, error) {
    48  	ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.Head")
    49  	defer span.End()
    50  	f.votesLock.Lock()
    51  	defer f.votesLock.Unlock()
    52  
    53  	calledHeadCount.Inc()
    54  
    55  	newBalances := justifiedStateBalances
    56  
    57  	// Using the write lock here because `updateCanonicalNodes` that gets called subsequently requires a write operation.
    58  	f.store.nodesLock.Lock()
    59  	defer f.store.nodesLock.Unlock()
    60  	deltas, newVotes, err := computeDeltas(ctx, f.store.nodesIndices, f.votes, f.balances, newBalances)
    61  	if err != nil {
    62  		return [32]byte{}, errors.Wrap(err, "Could not compute deltas")
    63  	}
    64  	f.votes = newVotes
    65  
    66  	if err := f.store.applyWeightChanges(ctx, justifiedEpoch, finalizedEpoch, deltas); err != nil {
    67  		return [32]byte{}, errors.Wrap(err, "Could not apply score changes")
    68  	}
    69  	f.balances = newBalances
    70  
    71  	return f.store.head(ctx, justifiedRoot)
    72  }
    73  
    74  // ProcessAttestation processes attestation for vote accounting, it iterates around validator indices
    75  // and update their votes accordingly.
    76  func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []uint64, blockRoot [32]byte, targetEpoch types.Epoch) {
    77  	ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.ProcessAttestation")
    78  	defer span.End()
    79  	f.votesLock.Lock()
    80  	defer f.votesLock.Unlock()
    81  
    82  	for _, index := range validatorIndices {
    83  		// Validator indices will grow the vote cache.
    84  		for index >= uint64(len(f.votes)) {
    85  			f.votes = append(f.votes, Vote{currentRoot: params.BeaconConfig().ZeroHash, nextRoot: params.BeaconConfig().ZeroHash})
    86  		}
    87  
    88  		// Newly allocated vote if the root fields are untouched.
    89  		newVote := f.votes[index].nextRoot == params.BeaconConfig().ZeroHash &&
    90  			f.votes[index].currentRoot == params.BeaconConfig().ZeroHash
    91  
    92  		// Vote gets updated if it's newly allocated or high target epoch.
    93  		if newVote || targetEpoch > f.votes[index].nextEpoch {
    94  			f.votes[index].nextEpoch = targetEpoch
    95  			f.votes[index].nextRoot = blockRoot
    96  		}
    97  	}
    98  
    99  	processedAttestationCount.Inc()
   100  }
   101  
   102  // ProcessBlock processes a new block by inserting it to the fork choice store.
   103  func (f *ForkChoice) ProcessBlock(
   104  	ctx context.Context,
   105  	slot types.Slot,
   106  	blockRoot, parentRoot, graffiti [32]byte,
   107  	justifiedEpoch, finalizedEpoch types.Epoch,
   108  ) error {
   109  	ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.ProcessBlock")
   110  	defer span.End()
   111  
   112  	return f.store.insert(ctx, slot, blockRoot, parentRoot, graffiti, justifiedEpoch, finalizedEpoch)
   113  }
   114  
   115  // Prune prunes the fork choice store with the new finalized root. The store is only pruned if the input
   116  // root is different than the current store finalized root, and the number of the store has met prune threshold.
   117  func (f *ForkChoice) Prune(ctx context.Context, finalizedRoot [32]byte) error {
   118  	return f.store.prune(ctx, finalizedRoot)
   119  }
   120  
   121  // Nodes returns the copied list of block nodes in the fork choice store.
   122  func (f *ForkChoice) Nodes() []*Node {
   123  	f.store.nodesLock.RLock()
   124  	defer f.store.nodesLock.RUnlock()
   125  
   126  	cpy := make([]*Node, len(f.store.nodes))
   127  	copy(cpy, f.store.nodes)
   128  	return cpy
   129  }
   130  
   131  // Store returns the fork choice store object which contains all the information regarding proto array fork choice.
   132  func (f *ForkChoice) Store() *Store {
   133  	f.store.nodesLock.Lock()
   134  	defer f.store.nodesLock.Unlock()
   135  	return f.store
   136  }
   137  
   138  // Node returns the copied node in the fork choice store.
   139  func (f *ForkChoice) Node(root [32]byte) *Node {
   140  	f.store.nodesLock.RLock()
   141  	defer f.store.nodesLock.RUnlock()
   142  
   143  	index, ok := f.store.nodesIndices[root]
   144  	if !ok {
   145  		return nil
   146  	}
   147  
   148  	return copyNode(f.store.nodes[index])
   149  }
   150  
   151  // HasNode returns true if the node exists in fork choice store,
   152  // false else wise.
   153  func (f *ForkChoice) HasNode(root [32]byte) bool {
   154  	f.store.nodesLock.RLock()
   155  	defer f.store.nodesLock.RUnlock()
   156  
   157  	_, ok := f.store.nodesIndices[root]
   158  	return ok
   159  }
   160  
   161  // HasParent returns true if the node parent exists in fork choice store,
   162  // false else wise.
   163  func (f *ForkChoice) HasParent(root [32]byte) bool {
   164  	f.store.nodesLock.RLock()
   165  	defer f.store.nodesLock.RUnlock()
   166  
   167  	i, ok := f.store.nodesIndices[root]
   168  	if !ok || i >= uint64(len(f.store.nodes)) {
   169  		return false
   170  	}
   171  
   172  	return f.store.nodes[i].parent != NonExistentNode
   173  }
   174  
   175  // IsCanonical returns true if the given root is part of the canonical chain.
   176  func (f *ForkChoice) IsCanonical(root [32]byte) bool {
   177  	f.store.nodesLock.RLock()
   178  	defer f.store.nodesLock.RUnlock()
   179  
   180  	return f.store.canonicalNodes[root]
   181  }
   182  
   183  // AncestorRoot returns the ancestor root of input block root at a given slot.
   184  func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot types.Slot) ([]byte, error) {
   185  	ctx, span := trace.StartSpan(ctx, "protoArray.AncestorRoot")
   186  	defer span.End()
   187  
   188  	f.store.nodesLock.RLock()
   189  	defer f.store.nodesLock.RUnlock()
   190  
   191  	i, ok := f.store.nodesIndices[root]
   192  	if !ok {
   193  		return nil, errors.New("node does not exist")
   194  	}
   195  	if i >= uint64(len(f.store.nodes)) {
   196  		return nil, errors.New("node index out of range")
   197  	}
   198  
   199  	for f.store.nodes[i].slot > slot {
   200  		if ctx.Err() != nil {
   201  			return nil, ctx.Err()
   202  		}
   203  
   204  		i = f.store.nodes[i].parent
   205  
   206  		if i >= uint64(len(f.store.nodes)) {
   207  			return nil, errors.New("node index out of range")
   208  		}
   209  	}
   210  
   211  	return f.store.nodes[i].root[:], nil
   212  }
   213  
   214  // PruneThreshold of fork choice store.
   215  func (s *Store) PruneThreshold() uint64 {
   216  	return s.pruneThreshold
   217  }
   218  
   219  // JustifiedEpoch of fork choice store.
   220  func (s *Store) JustifiedEpoch() types.Epoch {
   221  	return s.justifiedEpoch
   222  }
   223  
   224  // FinalizedEpoch of fork choice store.
   225  func (s *Store) FinalizedEpoch() types.Epoch {
   226  	return s.finalizedEpoch
   227  }
   228  
   229  // Nodes of fork choice store.
   230  func (s *Store) Nodes() []*Node {
   231  	s.nodesLock.RLock()
   232  	defer s.nodesLock.RUnlock()
   233  	return s.nodes
   234  }
   235  
   236  // NodesIndices of fork choice store.
   237  func (s *Store) NodesIndices() map[[32]byte]uint64 {
   238  	s.nodesLock.RLock()
   239  	defer s.nodesLock.RUnlock()
   240  	return s.nodesIndices
   241  }
   242  
   243  // head starts from justified root and then follows the best descendant links
   244  // to find the best block for head.
   245  func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, error) {
   246  	ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.head")
   247  	defer span.End()
   248  
   249  	// Justified index has to be valid in node indices map, and can not be out of bound.
   250  	justifiedIndex, ok := s.nodesIndices[justifiedRoot]
   251  	if !ok {
   252  		return [32]byte{}, errUnknownJustifiedRoot
   253  	}
   254  	if justifiedIndex >= uint64(len(s.nodes)) {
   255  		return [32]byte{}, errInvalidJustifiedIndex
   256  	}
   257  
   258  	justifiedNode := s.nodes[justifiedIndex]
   259  	bestDescendantIndex := justifiedNode.bestDescendant
   260  	// If the justified node doesn't have a best descendent,
   261  	// the best node is itself.
   262  	if bestDescendantIndex == NonExistentNode {
   263  		bestDescendantIndex = justifiedIndex
   264  	}
   265  	if bestDescendantIndex >= uint64(len(s.nodes)) {
   266  		return [32]byte{}, errInvalidBestDescendantIndex
   267  	}
   268  
   269  	bestNode := s.nodes[bestDescendantIndex]
   270  
   271  	if !s.viableForHead(bestNode) {
   272  		return [32]byte{}, fmt.Errorf("head at slot %d with weight %d is not eligible, finalizedEpoch %d != %d, justifiedEpoch %d != %d",
   273  			bestNode.slot, bestNode.weight/10e9, bestNode.finalizedEpoch, s.finalizedEpoch, bestNode.justifiedEpoch, s.justifiedEpoch)
   274  	}
   275  
   276  	// Update metrics.
   277  	if bestNode.root != lastHeadRoot {
   278  		headChangesCount.Inc()
   279  		headSlotNumber.Set(float64(bestNode.slot))
   280  		lastHeadRoot = bestNode.root
   281  	}
   282  
   283  	// Update canonical mapping given the head root.
   284  	if err := s.updateCanonicalNodes(ctx, bestNode.root); err != nil {
   285  		return [32]byte{}, err
   286  	}
   287  
   288  	return bestNode.root, nil
   289  }
   290  
   291  // updateCanonicalNodes updates the canonical nodes mapping given the input block root.
   292  func (s *Store) updateCanonicalNodes(ctx context.Context, root [32]byte) error {
   293  	ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.updateCanonicalNodes")
   294  	defer span.End()
   295  
   296  	// Set the input node to canonical.
   297  	s.canonicalNodes[root] = true
   298  
   299  	// Get the input's parent node index.
   300  	i := s.nodesIndices[root]
   301  	n := s.nodes[i]
   302  	p := n.parent
   303  
   304  	for p != NonExistentNode {
   305  		if ctx.Err() != nil {
   306  			return ctx.Err()
   307  		}
   308  
   309  		// Get the parent node, if the node is already in canonical mapping,
   310  		// we can be sure rest of the ancestors are canonical. Exit early.
   311  		n = s.nodes[p]
   312  		if s.canonicalNodes[n.root] {
   313  			break
   314  		}
   315  
   316  		// Set parent node to canonical. Repeat until parent node index is undefined.
   317  		s.canonicalNodes[n.root] = true
   318  		p = n.parent
   319  	}
   320  
   321  	return nil
   322  }
   323  
   324  // insert registers a new block node to the fork choice store's node list.
   325  // It then updates the new node's parent with best child and descendant node.
   326  func (s *Store) insert(ctx context.Context,
   327  	slot types.Slot,
   328  	root, parent, graffiti [32]byte,
   329  	justifiedEpoch, finalizedEpoch types.Epoch) error {
   330  	ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.insert")
   331  	defer span.End()
   332  
   333  	s.nodesLock.Lock()
   334  	defer s.nodesLock.Unlock()
   335  
   336  	// Return if the block has been inserted into Store before.
   337  	if _, ok := s.nodesIndices[root]; ok {
   338  		return nil
   339  	}
   340  
   341  	index := uint64(len(s.nodes))
   342  	parentIndex, ok := s.nodesIndices[parent]
   343  	// Mark genesis block's parent as non existent.
   344  	if !ok {
   345  		parentIndex = NonExistentNode
   346  	}
   347  
   348  	n := &Node{
   349  		slot:           slot,
   350  		root:           root,
   351  		graffiti:       graffiti,
   352  		parent:         parentIndex,
   353  		justifiedEpoch: justifiedEpoch,
   354  		finalizedEpoch: finalizedEpoch,
   355  		bestChild:      NonExistentNode,
   356  		bestDescendant: NonExistentNode,
   357  		weight:         0,
   358  	}
   359  
   360  	s.nodesIndices[root] = index
   361  	s.nodes = append(s.nodes, n)
   362  
   363  	// Update parent with the best child and descendent only if it's available.
   364  	if n.parent != NonExistentNode {
   365  		if err := s.updateBestChildAndDescendant(parentIndex, index); err != nil {
   366  			return err
   367  		}
   368  	}
   369  
   370  	// Update metrics.
   371  	processedBlockCount.Inc()
   372  	nodeCount.Set(float64(len(s.nodes)))
   373  
   374  	return nil
   375  }
   376  
   377  // applyWeightChanges iterates backwards through the nodes in store. It checks all nodes parent
   378  // and its best child. For each node, it updates the weight with input delta and
   379  // back propagate the nodes delta to its parents delta. After scoring changes,
   380  // the best child is then updated along with best descendant.
   381  func (s *Store) applyWeightChanges(ctx context.Context, justifiedEpoch, finalizedEpoch types.Epoch, delta []int) error {
   382  	ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.applyWeightChanges")
   383  	defer span.End()
   384  
   385  	// The length of the nodes can not be different than length of the delta.
   386  	if len(s.nodes) != len(delta) {
   387  		return errInvalidDeltaLength
   388  	}
   389  
   390  	// Update the justified / finalized epochs in store if necessary.
   391  	if s.justifiedEpoch != justifiedEpoch || s.finalizedEpoch != finalizedEpoch {
   392  		s.justifiedEpoch = justifiedEpoch
   393  		s.finalizedEpoch = finalizedEpoch
   394  	}
   395  
   396  	// Iterate backwards through all index to node in store.
   397  	for i := len(s.nodes) - 1; i >= 0; i-- {
   398  		n := s.nodes[i]
   399  
   400  		// There is no need to adjust the balances or manage parent of the zero hash, it
   401  		// is an alias to the genesis block.
   402  		if n.root == params.BeaconConfig().ZeroHash {
   403  			continue
   404  		}
   405  
   406  		nodeDelta := delta[i]
   407  
   408  		if nodeDelta < 0 {
   409  			// A node's weight can not be negative but the delta can be negative.
   410  			if int(n.weight)+nodeDelta < 0 {
   411  				n.weight = 0
   412  			} else {
   413  				// Absolute value of node delta.
   414  				d := nodeDelta
   415  				if nodeDelta < 0 {
   416  					d *= -1
   417  				}
   418  				// Subtract node's weight.
   419  				n.weight -= uint64(d)
   420  			}
   421  		} else {
   422  			// Add node's weight.
   423  			n.weight += uint64(nodeDelta)
   424  		}
   425  
   426  		s.nodes[i] = n
   427  
   428  		// Update parent's best child and descendent if the node has a known parent.
   429  		if n.parent != NonExistentNode {
   430  			// Protection against node parent index out of bound. This should not happen.
   431  			if int(n.parent) >= len(delta) {
   432  				return errInvalidParentDelta
   433  			}
   434  			// Back propagate the nodes delta to its parent.
   435  			delta[n.parent] += nodeDelta
   436  		}
   437  	}
   438  
   439  	for i := len(s.nodes) - 1; i >= 0; i-- {
   440  		n := s.nodes[i]
   441  		if n.parent != NonExistentNode {
   442  			if int(n.parent) >= len(delta) {
   443  				return errInvalidParentDelta
   444  			}
   445  			if err := s.updateBestChildAndDescendant(n.parent, uint64(i)); err != nil {
   446  				return err
   447  			}
   448  		}
   449  	}
   450  
   451  	return nil
   452  }
   453  
   454  // updateBestChildAndDescendant updates parent node's best child and descendent.
   455  // It looks at input parent node and input child node and potentially modifies parent's best
   456  // child and best descendent indices.
   457  // There are four outcomes:
   458  // 1.)  The child is already the best child but it's now invalid due to a FFG change and should be removed.
   459  // 2.)  The child is already the best child and the parent is updated with the new best descendant.
   460  // 3.)  The child is not the best child but becomes the best child.
   461  // 4.)  The child is not the best child and does not become best child.
   462  func (s *Store) updateBestChildAndDescendant(parentIndex, childIndex uint64) error {
   463  
   464  	// Protection against parent index out of bound, this should not happen.
   465  	if parentIndex >= uint64(len(s.nodes)) {
   466  		return errInvalidNodeIndex
   467  	}
   468  	parent := s.nodes[parentIndex]
   469  
   470  	// Protection against child index out of bound, again this should not happen.
   471  	if childIndex >= uint64(len(s.nodes)) {
   472  		return errInvalidNodeIndex
   473  	}
   474  	child := s.nodes[childIndex]
   475  
   476  	// Is the child viable to become head? Based on justification and finalization rules.
   477  	childLeadsToViableHead, err := s.leadsToViableHead(child)
   478  	if err != nil {
   479  		return err
   480  	}
   481  
   482  	// Define 3 variables for the 3 outcomes mentioned above. This is to
   483  	// set `parent.bestChild` and `parent.bestDescendant` to. These
   484  	// aliases are to assist readability.
   485  	changeToNone := []uint64{NonExistentNode, NonExistentNode}
   486  	bestDescendant := child.bestDescendant
   487  	if bestDescendant == NonExistentNode {
   488  		bestDescendant = childIndex
   489  	}
   490  	changeToChild := []uint64{childIndex, bestDescendant}
   491  	noChange := []uint64{parent.bestChild, parent.bestDescendant}
   492  	var newParentChild []uint64
   493  
   494  	if parent.bestChild != NonExistentNode {
   495  		if parent.bestChild == childIndex && !childLeadsToViableHead {
   496  			// If the child is already the best child of the parent but it's not viable for head,
   497  			// we should remove it. (Outcome 1)
   498  			newParentChild = changeToNone
   499  		} else if parent.bestChild == childIndex {
   500  			// If the child is already the best child of the parent, set it again to ensure best
   501  			// descendent of the parent is updated. (Outcome 2)
   502  			newParentChild = changeToChild
   503  		} else {
   504  			// Protection against parent's best child going out of bound.
   505  			if parent.bestChild > uint64(len(s.nodes)) {
   506  				return errInvalidBestDescendantIndex
   507  			}
   508  			bestChild := s.nodes[parent.bestChild]
   509  			// Is current parent's best child viable to be head? Based on justification and finalization rules.
   510  			bestChildLeadsToViableHead, err := s.leadsToViableHead(bestChild)
   511  			if err != nil {
   512  				return err
   513  			}
   514  
   515  			if childLeadsToViableHead && !bestChildLeadsToViableHead {
   516  				// The child leads to a viable head, but the current parent's best child doesnt.
   517  				newParentChild = changeToChild
   518  			} else if !childLeadsToViableHead && bestChildLeadsToViableHead {
   519  				// The child doesn't lead to a viable head, the current parent's best child does.
   520  				newParentChild = noChange
   521  			} else if child.weight == bestChild.weight {
   522  				// If both are viable, compare their weights.
   523  				// Tie-breaker of equal weights by root.
   524  				if bytes.Compare(child.root[:], bestChild.root[:]) > 0 {
   525  					newParentChild = changeToChild
   526  				} else {
   527  					newParentChild = noChange
   528  				}
   529  			} else {
   530  				// Choose winner by weight.
   531  				if child.weight > bestChild.weight {
   532  					newParentChild = changeToChild
   533  				} else {
   534  					newParentChild = noChange
   535  				}
   536  			}
   537  		}
   538  	} else {
   539  		if childLeadsToViableHead {
   540  			// If parent doesn't have a best child and the child is viable.
   541  			newParentChild = changeToChild
   542  		} else {
   543  			// If parent doesn't have a best child and the child is not viable.
   544  			newParentChild = noChange
   545  		}
   546  	}
   547  
   548  	// Update parent with the outcome.
   549  	parent.bestChild = newParentChild[0]
   550  	parent.bestDescendant = newParentChild[1]
   551  	s.nodes[parentIndex] = parent
   552  
   553  	return nil
   554  }
   555  
   556  // prune prunes the store with the new finalized root. The tree is only
   557  // pruned if the input finalized root are different than the one in stored and
   558  // the number of the nodes in store has met prune threshold.
   559  func (s *Store) prune(ctx context.Context, finalizedRoot [32]byte) error {
   560  	ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.prune")
   561  	defer span.End()
   562  
   563  	s.nodesLock.Lock()
   564  	defer s.nodesLock.Unlock()
   565  
   566  	// The node would have seen finalized root or else it'd
   567  	// be able to prune it.
   568  	finalizedIndex, ok := s.nodesIndices[finalizedRoot]
   569  	if !ok {
   570  		return errUnknownFinalizedRoot
   571  	}
   572  
   573  	// The number of the nodes has not met the prune threshold.
   574  	// Pruning at small numbers incurs more cost than benefit.
   575  	if finalizedIndex < s.pruneThreshold {
   576  		return nil
   577  	}
   578  
   579  	// Remove the key/values from indices mapping on to be pruned nodes.
   580  	// These nodes are before the finalized index.
   581  	for i := uint64(0); i < finalizedIndex; i++ {
   582  		if int(i) >= len(s.nodes) {
   583  			return errInvalidNodeIndex
   584  		}
   585  		delete(s.nodesIndices, s.nodes[i].root)
   586  	}
   587  
   588  	// Finalized index can not be greater than the length of the node.
   589  	if int(finalizedIndex) >= len(s.nodes) {
   590  		return errors.New("invalid finalized index")
   591  	}
   592  	s.nodes = s.nodes[finalizedIndex:]
   593  
   594  	// Adjust indices to node mapping.
   595  	for k, v := range s.nodesIndices {
   596  		s.nodesIndices[k] = v - finalizedIndex
   597  	}
   598  
   599  	// Iterate through existing nodes and adjust its parent/child indices with the newly pruned layout.
   600  	for i, node := range s.nodes {
   601  		if node.parent != NonExistentNode {
   602  			// If the node's parent is less than finalized index, set it to non existent.
   603  			if node.parent >= finalizedIndex {
   604  				node.parent -= finalizedIndex
   605  			} else {
   606  				node.parent = NonExistentNode
   607  			}
   608  		}
   609  		if node.bestChild != NonExistentNode {
   610  			if node.bestChild < finalizedIndex {
   611  				return errInvalidBestChildIndex
   612  			}
   613  			node.bestChild -= finalizedIndex
   614  		}
   615  		if node.bestDescendant != NonExistentNode {
   616  			if node.bestDescendant < finalizedIndex {
   617  				return errInvalidBestDescendantIndex
   618  			}
   619  			node.bestDescendant -= finalizedIndex
   620  		}
   621  
   622  		s.nodes[i] = node
   623  	}
   624  
   625  	prunedCount.Inc()
   626  
   627  	return nil
   628  }
   629  
   630  // leadsToViableHead returns true if the node or the best descendent of the node is viable for head.
   631  // Any node with diff finalized or justified epoch than the ones in fork choice store
   632  // should not be viable to head.
   633  func (s *Store) leadsToViableHead(node *Node) (bool, error) {
   634  	var bestDescendentViable bool
   635  	bestDescendentIndex := node.bestDescendant
   636  
   637  	// If the best descendant is not part of the leaves.
   638  	if bestDescendentIndex != NonExistentNode {
   639  		// Protection against out of bound, best descendent index can not be
   640  		// exceeds length of nodes list.
   641  		if bestDescendentIndex >= uint64(len(s.nodes)) {
   642  			return false, errInvalidBestDescendantIndex
   643  		}
   644  
   645  		bestDescendentNode := s.nodes[bestDescendentIndex]
   646  		bestDescendentViable = s.viableForHead(bestDescendentNode)
   647  	}
   648  
   649  	// The node is viable as long as the best descendent is viable.
   650  	return bestDescendentViable || s.viableForHead(node), nil
   651  }
   652  
   653  // viableForHead returns true if the node is viable to head.
   654  // Any node with diff finalized or justified epoch than the ones in fork choice store
   655  // should not be viable to head.
   656  func (s *Store) viableForHead(node *Node) bool {
   657  	// `node` is viable if its justified epoch and finalized epoch are the same as the one in `Store`.
   658  	// It's also viable if we are in genesis epoch.
   659  	justified := s.justifiedEpoch == node.justifiedEpoch || s.justifiedEpoch == 0
   660  	finalized := s.finalizedEpoch == node.finalizedEpoch || s.finalizedEpoch == 0
   661  
   662  	return justified && finalized
   663  }