Skip to content

Commit

Permalink
chore(lib/grandpa): update grandpa message types to match substrate (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
noot committed Apr 22, 2021
1 parent 1a3dea2 commit 4f8a37b
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 115 deletions.
7 changes: 5 additions & 2 deletions lib/grandpa/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ var ErrCannotDecodeSubround = errors.New("cannot decode invalid subround value")
// ErrInvalidMessageType is returned when a network.Message cannot be decoded
var ErrInvalidMessageType = errors.New("cannot decode invalid message type")

// ErrNotFinalizationMessage is returned when calling GetFinalizedHash on a message that isn't a FinalizationMessage
var ErrNotFinalizationMessage = errors.New("cannot get finalized hash from VoteMessage")
// ErrNotCommitMessage is returned when calling GetFinalizedHash on a message that isn't a CommitMessage
var ErrNotCommitMessage = errors.New("cannot get finalized hash from VoteMessage")

// ErrNoJustification is returned when no justification can be found for a block, ie. it has not been finalized
var ErrNoJustification = errors.New("no justification found for block")
Expand All @@ -92,3 +92,6 @@ var ErrCatchUpResponseNotCompletable = errors.New("catch up response is not comp

// ErrServicePaused is returned if the service is paused and waiting for catch up messages
var ErrServicePaused = errors.New("service is paused")

// ErrPrecommitSignatureMismatch is returned when the number of precommits and signatures in a CommitMessage do not match
var ErrPrecommitSignatureMismatch = errors.New("number of precommits does not match number of signatures")
19 changes: 17 additions & 2 deletions lib/grandpa/grandpa.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,12 +387,27 @@ func (s *Service) playGrandpaRound() error {

// if primary, broadcast the best final candidate from the previous round
if bytes.Equal(primary.key.Encode(), s.keypair.Public().Encode()) {
msg, err := s.newFinalizationMessage(s.head, s.state.round-1).ToConsensusMessage()
msg, err := s.newCommitMessage(s.head, s.state.round-1).ToConsensusMessage()
if err != nil {
logger.Error("failed to encode finalization message", "error", err)
} else {
s.network.SendMessage(msg)
}

primProposal, err := s.createVoteMessage(&Vote{
hash: s.head.Hash(),
number: uint32(s.head.Number.Int64()),
}, primaryProposal, s.keypair)
if err != nil {
logger.Error("failed to create primary proposal message", "error", err)
} else {
msg, err = primProposal.ToConsensusMessage()
if err != nil {
logger.Error("failed to encode finalization message", "error", err)
} else {
s.network.SendMessage(msg)
}
}
}

logger.Debug("receiving pre-vote messages...")
Expand Down Expand Up @@ -584,7 +599,7 @@ func (s *Service) attemptToFinalize() error {
votes := s.getDirectVotes(precommit)
logger.Debug("finalized block!!!", "setID", s.state.setID, "round", s.state.round, "hash", s.head.Hash(),
"precommits #", pc, "votes for bfc #", votes[*bfc], "total votes for bfc", pc, "precommits", s.precommits)
msg, err := s.newFinalizationMessage(s.head, s.state.round).ToConsensusMessage()
msg, err := s.newCommitMessage(s.head, s.state.round).ToConsensusMessage()
if err != nil {
return err
}
Expand Down
199 changes: 174 additions & 25 deletions lib/grandpa/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package grandpa
import (
"bytes"
"fmt"
"io"
"math/big"

"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/types"
Expand All @@ -36,11 +38,10 @@ type GrandpaMessage interface { //nolint

var (
voteType byte = 0
precommitType byte = 1 // TODO: precommitType is now part of voteType
commitType byte = 1
neighbourType byte = 2
catchUpRequestType byte = 3
catchUpResponseType byte = 4
finalizationType byte = 5 // TODO: this is actually 1
)

// FullVote represents a vote with additional information about the state
Expand All @@ -54,6 +55,7 @@ type FullVote struct {

// SignedMessage represents a block hash and number signed by an authority
type SignedMessage struct {
Stage subround // 0 for pre-vote, 1 for pre-commit, 2 for primary proposal
Hash common.Hash
Number uint32
Signature [64]byte // ed25519.SignatureLength
Expand All @@ -65,18 +67,65 @@ func (m *SignedMessage) String() string {
return fmt.Sprintf("hash=%s number=%d authorityID=0x%x", m.Hash, m.Number, m.AuthorityID)
}

// Decode SCALE decodes the data into a SignedMessage
func (m *SignedMessage) Decode(r io.Reader) (err error) {
m.Stage, err = subround(0).Decode(r)
if err != nil {
return err
}

vote, err := new(Vote).Decode(r)
if err != nil {
return err
}

m.Hash = vote.hash
m.Number = vote.number

sig, err := common.Read64Bytes(r)
if err != nil {
return err
}

copy(m.Signature[:], sig[:])

id, err := common.Read32Bytes(r)
if err != nil {
return err
}

copy(m.AuthorityID[:], id[:])
return nil
}

// VoteMessage represents a network-level vote message
// https://github.com/paritytech/substrate/blob/master/client/finality-grandpa/src/communication/gossip.rs#L336
type VoteMessage struct {
Round uint64
SetID uint64
Stage subround // 0 for pre-vote, 1 for pre-commit
Message *SignedMessage
}

// Type returns voteType or precommitType
// Decode SCALE decodes the data into a VoteMessage
func (v *VoteMessage) Decode(r io.Reader) (err error) {
v.Round, err = common.ReadUint64(r)
if err != nil {
return err
}

v.SetID, err = common.ReadUint64(r)
if err != nil {
return err
}

v.Message = new(SignedMessage)
err = v.Message.Decode(r)
return err
}

// Type returns voteType
func (v *VoteMessage) Type() byte {
return byte(v.Stage)
return voteType
}

// ToConsensusMessage converts the VoteMessage into a network-level consensus message
Expand All @@ -86,9 +135,8 @@ func (v *VoteMessage) ToConsensusMessage() (*ConsensusMessage, error) {
return nil, err
}

typ := byte(v.Stage)
return &ConsensusMessage{
Data: append([]byte{typ}, enc...),
Data: append([]byte{voteType}, enc...),
}, nil
}

Expand Down Expand Up @@ -117,38 +165,139 @@ func (m *NeighbourMessage) Type() byte {
return neighbourType
}

// FinalizationMessage represents a network finalization message
type FinalizationMessage struct {
Round uint64
Vote *Vote
Justification []*SignedPrecommit
// AuthData represents signature data within a CommitMessage to be paired with a Precommit
type AuthData struct {
Signature [64]byte
AuthorityID ed25519.PublicKeyBytes
}

// Encode SCALE encodes the AuthData
func (d *AuthData) Encode() ([]byte, error) {
return append(d.Signature[:], d.AuthorityID[:]...), nil
}

// Decode SCALE decodes the data into an AuthData
func (d *AuthData) Decode(r io.Reader) error {
sig, err := common.Read64Bytes(r)
if err != nil {
return err
}

copy(d.Signature[:], sig[:])

id, err := common.Read32Bytes(r)
if err != nil {
return err
}

copy(d.AuthorityID[:], id[:])
return nil
}

// Type returns finalizationType
func (f *FinalizationMessage) Type() byte {
return finalizationType
// CommitMessage represents a network finalization message
type CommitMessage struct {
Round uint64
SetID uint64
Vote *Vote
Precommits []*Vote
AuthData []*AuthData
}

// ToConsensusMessage converts the FinalizationMessage into a network-level consensus message
func (f *FinalizationMessage) ToConsensusMessage() (*ConsensusMessage, error) {
// Decode SCALE decodes the data into a CommitMessage
func (f *CommitMessage) Decode(r io.Reader) (err error) {
f.Round, err = common.ReadUint64(r)
if err != nil {
return err
}

f.SetID, err = common.ReadUint64(r)
if err != nil {
return err
}

f.Vote, err = new(Vote).Decode(r)
if err != nil {
return err
}

sd := &scale.Decoder{Reader: r}
numPrecommits, err := sd.Decode(new(big.Int))
if err != nil {
return err
}

f.Precommits = make([]*Vote, numPrecommits.(*big.Int).Int64())
for i := range f.Precommits {
f.Precommits[i], err = new(Vote).Decode(r)
if err != nil {
return err
}
}

numAuthData, err := sd.Decode(new(big.Int))
if err != nil {
return err
}

if numAuthData.(*big.Int).Cmp(numPrecommits.(*big.Int)) != 0 {
return ErrPrecommitSignatureMismatch
}

f.AuthData = make([]*AuthData, numAuthData.(*big.Int).Int64())
for i := range f.AuthData {
f.AuthData[i] = new(AuthData)
err = f.AuthData[i].Decode(r)
if err != nil {
return err
}
}

return nil
}

// Type returns commitType
func (f *CommitMessage) Type() byte {
return commitType
}

// ToConsensusMessage converts the CommitMessage into a network-level consensus message
func (f *CommitMessage) ToConsensusMessage() (*ConsensusMessage, error) {
enc, err := scale.Encode(f)
if err != nil {
return nil, err
}

return &ConsensusMessage{
Data: append([]byte{finalizationType}, enc...),
Data: append([]byte{commitType}, enc...),
}, nil
}

func (s *Service) newFinalizationMessage(header *types.Header, round uint64) *FinalizationMessage {
return &FinalizationMessage{
Round: round,
Vote: NewVoteFromHeader(header),
Justification: s.justification[round],
func (s *Service) newCommitMessage(header *types.Header, round uint64) *CommitMessage {
just := s.justification[round]
precommits, authData := justificationToCompact(just)
return &CommitMessage{
Round: round,
Vote: NewVoteFromHeader(header),
Precommits: precommits,
AuthData: authData,
}
}

func justificationToCompact(just []*SignedPrecommit) ([]*Vote, []*AuthData) {
precommits := make([]*Vote, len(just))
authData := make([]*AuthData, len(just))

for i, j := range just {
precommits[i] = j.Vote
authData[i] = &AuthData{
Signature: j.Signature,
AuthorityID: j.AuthorityID,
}
}

return precommits, authData
}

type catchUpRequest struct {
Round uint64
SetID uint64
Expand Down Expand Up @@ -179,8 +328,8 @@ func (r *catchUpRequest) ToConsensusMessage() (*ConsensusMessage, error) {
}

type catchUpResponse struct {
Round uint64
SetID uint64
Round uint64
PreVoteJustification []*SignedPrecommit
PreCommitJustification []*SignedPrecommit
Hash common.Hash
Expand Down Expand Up @@ -227,8 +376,8 @@ func (s *Service) newCatchUpResponse(round, setID uint64) (*catchUpResponse, err
pcj := d.([]*SignedPrecommit)

return &catchUpResponse{
Round: round,
SetID: setID,
Round: round,
PreVoteJustification: pvj,
PreCommitJustification: pcj,
Hash: header.Hash(),
Expand Down
Loading

0 comments on commit 4f8a37b

Please sign in to comment.