Skip to content

Commit

Permalink
Part 9 of proto array fork choice - get head (#4643)
Browse files Browse the repository at this point in the history
  • Loading branch information
terencechain committed Jan 24, 2020
1 parent a78defc commit b313b46
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 0 deletions.
37 changes: 37 additions & 0 deletions beacon-chain/forkchoice/protoarray/nodes.go
Expand Up @@ -4,12 +4,49 @@ import (
"bytes"
"context"
"errors"
"fmt"
"math"

"github.com/prysmaticlabs/prysm/shared/params"
"go.opencensus.io/trace"
)

// head starts from justified root and then follows the best descendant links
// to find the best block for head.
func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, error) {
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.head")
defer span.End()

// Justified index has to be valid in node indices map, and can not be out of bound.
justifiedIndex, ok := s.nodeIndices[justifiedRoot]
if !ok {
return [32]byte{}, errUnknownJustifiedRoot
}
if justifiedIndex >= uint64(len(s.nodes)) {
return [32]byte{}, errInvalidJustifiedIndex
}

justifiedNode := s.nodes[justifiedIndex]
bestDescendantIndex := justifiedNode.bestDescendant
// If the justified node doesn't have a best descendent,
// the best node is itself.
if bestDescendantIndex == nonExistentNode {
bestDescendantIndex = justifiedIndex
}
if bestDescendantIndex >= uint64(len(s.nodes)) {
return [32]byte{}, errInvalidBestDescendantIndex
}

bestNode := s.nodes[bestDescendantIndex]
//
if !s.viableForHead(ctx, bestNode) {
return [32]byte{}, fmt.Errorf("after tree filter, best node can't be head, finalized epochs %d != %d, justified epoch %d != %d",
bestNode.finalizedEpoch, s.finalizedEpoch, bestNode.justifiedEpoch, s.justifiedEpoch)
}

return bestNode.root, nil
}

// insert registers a new block node to the fork choice store's node list.
// It then updates the new node's parent with best child and descendant node.
func (s *Store) insert(ctx context.Context,
Expand Down
56 changes: 56 additions & 0 deletions beacon-chain/forkchoice/protoarray/nodes_test.go
Expand Up @@ -5,6 +5,62 @@ import (
"testing"
)

func TestStore_Head_UnknownJustifiedRoot(t *testing.T) {
s := &Store{nodeIndices: make(map[[32]byte]uint64)}

if _, err := s.head(context.Background(), [32]byte{}); err.Error() != errUnknownJustifiedRoot.Error() {
t.Fatal("Did not get wanted error")
}
}

func TestStore_Head_UnknownJustifiedIndex(t *testing.T) {
r := [32]byte{'A'}
indices := make(map[[32]byte]uint64)
indices[r] = 1
s := &Store{nodeIndices: indices}

if _, err := s.head(context.Background(), r); err.Error() != errInvalidJustifiedIndex.Error() {
t.Fatal("Did not get wanted error")
}
}

func TestStore_Head_Itself(t *testing.T) {
r := [32]byte{'A'}
indices := make(map[[32]byte]uint64)
indices[r] = 0

// Since the justified node does not have a best descendant so the best node
// is itself.
s := &Store{nodeIndices: indices, nodes: []*Node{{root: r, bestDescendant: nonExistentNode}}}
h, err := s.head(context.Background(), r)
if err != nil {
t.Fatal("Did not get wanted error")
}

if h != r {
t.Error("Did not get wanted head")
}
}

func TestStore_Head_BestDescendant(t *testing.T) {
r := [32]byte{'A'}
best := [32]byte{'B'}
indices := make(map[[32]byte]uint64)
indices[r] = 0

// Since the justified node's best descendent is at index 1 and it's root is `best`,
// the head should be `best`.
s := &Store{nodeIndices: indices, nodes: []*Node{{root: r, bestDescendant: 1}, {root: best}}}
h, err := s.head(context.Background(), r)
if err != nil {
t.Fatal("Did not get wanted error")
}

if h != best {
t.Error("Did not get wanted head")
}
}

func TestStore_Insert_UnknownParent(t *testing.T) {
// The new node does not have a parent.
s := &Store{nodeIndices: make(map[[32]byte]uint64)}
Expand Down

0 comments on commit b313b46

Please sign in to comment.