diff --git a/lib/blocktree/errors.go b/lib/blocktree/errors.go index 5af63f34b1..90e66c0744 100644 --- a/lib/blocktree/errors.go +++ b/lib/blocktree/errors.go @@ -45,5 +45,7 @@ var ( // ErrNoCommonAncestor is returned when a common ancestor cannot be found between two nodes ErrNoCommonAncestor = errors.New("no common ancestor between two nodes") + ErrNotADescendant = errors.New("not a descendant") + errUnexpectedNumber = errors.New("block number is not parent number + 1") ) diff --git a/lib/grandpa/grandpa.go b/lib/grandpa/grandpa.go index c9747e81d6..30b09f7716 100644 --- a/lib/grandpa/grandpa.go +++ b/lib/grandpa/grandpa.go @@ -678,13 +678,43 @@ func (s *Service) deleteVote(key ed25519.PublicKeyBytes, stage Subround) { } } -// determinePreVote determines what block is our pre-voted block for the current round -func (s *Service) determinePreVote() (*Vote, error) { - var vote *Vote +func (s *Service) determineBestHeaderToVote(bestBlockHeader, + latestFinalizedHeader *types.Header) (vote *types.GrandpaVote, err error) { + genesisHash := s.blockState.GenesisHash() + + bestBlockHash := bestBlockHeader.Hash() + finalizedHash := latestFinalizedHeader.Hash() + + isGenesisHash := genesisHash.Equal(bestBlockHash) + isFinalizedHash := finalizedHash.Equal(bestBlockHash) + + if isGenesisHash || isFinalizedHash { + return NewVoteFromHeader(bestBlockHeader), nil + } + + isDescendant, err := s.blockState.IsDescendantOf(finalizedHash, bestBlockHash) + if err != nil { + return nil, fmt.Errorf("determining ancestry: %w", err) + } + + if !isDescendant { + return nil, fmt.Errorf("%w: %s of %s", + blocktree.ErrNotADescendant, bestBlockHash.Short(), finalizedHash.Short()) + } + return NewVoteFromHeader(bestBlockHeader), nil +} + +// determinePreVote determines what block is our pre-voted block for the current round +func (s *Service) determinePreVote() (vote *Vote, err error) { bestBlockHeader, err := s.blockState.BestBlockHeader() if err != nil { - return nil, fmt.Errorf("cannot get best block header: %w", err) + return nil, fmt.Errorf("getting best block: %w", err) + } + + vote, err = s.determineBestHeaderToVote(bestBlockHeader, s.head) + if err != nil { + return nil, fmt.Errorf("determining best block header to vote: %w", err) } // if we receive a vote message from the primary with a @@ -695,11 +725,11 @@ func (s *Service) determinePreVote() (*Vote, error) { prm, has := s.loadVote(primary.PublicKeyBytes(), prevote) if has && prm.Vote.Number >= uint32(s.head.Number) { vote = &prm.Vote - } else { - vote = NewVoteFromHeader(bestBlockHeader) } - nextChange, err := s.grandpaState.NextGrandpaAuthorityChange(bestBlockHeader.Hash(), bestBlockHeader.Number) + nextChange, err := s.grandpaState.NextGrandpaAuthorityChange( + vote.Hash, uint(vote.Number)) + if errors.Is(err, state.ErrNoNextAuthorityChange) { return vote, nil } else if err != nil { @@ -709,7 +739,7 @@ func (s *Service) determinePreVote() (*Vote, error) { if uint(vote.Number) > nextChange { header, err := s.blockState.GetHeaderByNumber(nextChange) if err != nil { - return nil, err + return nil, fmt.Errorf("getting header by number: %w", err) } vote = NewVoteFromHeader(header) } @@ -961,7 +991,8 @@ func (s *Service) getPreVotedBlock() (Vote, error) { // if there are multiple, find the one with the highest number and return it highest := Vote{ - Number: uint32(0), + Hash: s.head.Hash(), + Number: uint32(s.head.Number), } for h, n := range blocks { @@ -1005,7 +1036,8 @@ func (s *Service) getGrandpaGHOST() (Vote, error) { // if there are multiple, find the one with the highest number and return it highest := Vote{ - Number: uint32(0), + Hash: s.head.Hash(), + Number: uint32(s.head.Number), } for h, n := range blocks { diff --git a/lib/grandpa/grandpa_test.go b/lib/grandpa/grandpa_test.go index dda0511f9d..7c9daffcfd 100644 --- a/lib/grandpa/grandpa_test.go +++ b/lib/grandpa/grandpa_test.go @@ -966,6 +966,7 @@ func TestDeterminePreVote_NoPrimaryPreVote(t *testing.T) { header, err := st.Block.BestBlockHeader() require.NoError(t, err) + require.Equal(t, header.Hash(), pv.Hash) } diff --git a/lib/grandpa/message.go b/lib/grandpa/message.go index 9ab086d67d..3129ed8d61 100644 --- a/lib/grandpa/message.go +++ b/lib/grandpa/message.go @@ -56,6 +56,10 @@ type VoteMessage struct { Message SignedMessage } +func (v VoteMessage) String() string { + return fmt.Sprintf("round=%d, setID=%d, message=%s", v.Round, v.SetID, v.Message) +} + // Index Returns VDT index func (VoteMessage) Index() uint { return 0 } diff --git a/lib/grandpa/message_handler.go b/lib/grandpa/message_handler.go index fef28cccf5..34b09694cb 100644 --- a/lib/grandpa/message_handler.go +++ b/lib/grandpa/message_handler.go @@ -328,7 +328,7 @@ func (h *MessageHandler) verifyCommitMessageJustification(fm *CommitMessage) err return fmt.Errorf("cannot verify ancestry of highest finalised block: %w", err) } if !isDescendant { - return errVoteBlockMismatch + return fmt.Errorf("%w", errVoteBlockMismatch) } eqvVoters := getEquivocatoryVoters(fm.AuthData) diff --git a/lib/grandpa/round_test.go b/lib/grandpa/round_test.go index fd9729e602..4597b059a0 100644 --- a/lib/grandpa/round_test.go +++ b/lib/grandpa/round_test.go @@ -4,7 +4,6 @@ package grandpa import ( - //"fmt" "math/rand" "sync" "testing"