Skip to content

Commit

Permalink
feat: implements before best block by N voting rule
Browse files Browse the repository at this point in the history
  • Loading branch information
EclesioMeloJunior committed Aug 10, 2022
1 parent 882dfa1 commit 4d56197
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 62 deletions.
16 changes: 8 additions & 8 deletions chain/dev/config.toml
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
[global]
basepath = "~/.gossamer/dev"
basepath = "./tmp/dev"
log = "info"
metrics-address = ":9876"

[log]
core = ""
network = ""
network = "trace"
rpc = ""
state = ""
runtime = ""
babe = ""
grandpa = ""
sync = ""
grandpa = "trace"
sync = "trace"
digest = ""

[init]
genesis = "./chain/dev/genesis-spec.json"
genesis = "./chain/dev/genesis.json"

[account]
key = "alice"
key = "bob"
unlock = ""

[core]
roles = 4
babe-authority = true
grandpa-authority = true
babe-lead = true
babe-lead = false

[network]
port = 7001
port = 7002
nobootstrap = false
nomdns = false

Expand Down
147 changes: 104 additions & 43 deletions chain/dev/genesis.json

Large diffs are not rendered by default.

56 changes: 45 additions & 11 deletions lib/grandpa/grandpa.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,44 @@ func (s *Service) deleteVote(key ed25519.PublicKeyBytes, stage Subround) {
}
}

// implements `BeforeBestBlockBy` a custom voting rule that guarantees that our vote is always
// behind the best block by at least N blocks, unless the base number is < N blocks behind the
// best, in which case it votes for the base.
// (https://github.com/paritytech/substrate/blob/1fd71c7845d6c28c532795ec79106d959dd1fe30/client/finality-grandpa/src/voting_rule.rs#L92)
func (s *Service) determineBestHeaderToPrevote(finalizedHeader, bestBlockHeader *types.Header) (
headerToPrevote *types.Header, err error) {
gensisHash := s.blockState.GenesisHash()
isGenesisHash := gensisHash.Equal(bestBlockHeader.Hash())
if isGenesisHash || finalizedHeader.Hash().Equal(bestBlockHeader.Hash()) {
return bestBlockHeader, nil
}

isDescendant, err := s.blockState.IsDescendantOf(finalizedHeader.Hash(), bestBlockHeader.Hash())
if err != nil {
return headerToPrevote, fmt.Errorf("determine ancestry: %w", err)
}

if !isDescendant {
return headerToPrevote, fmt.Errorf("%w: %s is not ancestor of %s",
blocktree.ErrNoCommonAncestor, bestBlockHeader.Hash().Short(), bestBlockHeader.Hash().Short())
}

headerToPrevote = bestBlockHeader
for i := 0; i < 2; i++ {
headerToPrevote, err := s.blockState.GetHeader(headerToPrevote.ParentHash)
if err != nil {
return headerToPrevote, fmt.Errorf("get parent header: %w", err)
}

isGenesisHash := gensisHash.Equal(headerToPrevote.Hash())
if finalizedHeader.Hash().Equal(headerToPrevote.Hash()) || isGenesisHash {
break
}
}

return headerToPrevote, nil
}

// determinePreVote determines what block is our pre-voted block for the current round
func (s *Service) determinePreVote() (*Vote, error) {
var vote *Vote
Expand All @@ -730,19 +768,15 @@ func (s *Service) determinePreVote() (*Vote, error) {
return nil, fmt.Errorf("cannot get best block header: %w", err)
}

// if we receive a vote message from the primary with a
// block that's greater than or equal to the current pre-voted block
// and greater than the best final candidate from the last round, we choose that.
// otherwise, we simply choose the head of our chain.
primary := s.derivePrimary()
prm, has := s.loadVote(primary.PublicKeyBytes(), prevote)
if has && prm.Vote.Number >= uint32(s.head.Number) {
vote = &prm.Vote
} else {
vote = NewVoteFromHeader(bestBlockHeader)
headerToPrevote, err := s.determineBestHeaderToPrevote(s.head, bestBlockHeader)
if err != nil {
return nil, fmt.Errorf("determine best hash to prevote: %w", err)
}

nextChange, err := s.grandpaState.NextGrandpaAuthorityChange(bestBlockHeader.Hash(), bestBlockHeader.Number)
vote = NewVoteFromHeader(headerToPrevote)
nextChange, err := s.grandpaState.NextGrandpaAuthorityChange(
headerToPrevote.Hash(), headerToPrevote.Number)

if errors.Is(err, state.ErrNoNextAuthorityChange) {
return vote, nil
} else if err != nil {
Expand Down

0 comments on commit 4d56197

Please sign in to comment.