Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions common/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,15 @@ type QCDeserializer interface {
// DeserializeQuorumCertificate parses the given bytes and initializes a QuorumCertificate.
// Returns an error upon failure.
DeserializeQuorumCertificate(bytes []byte) (QuorumCertificate, error)

// ParseQuorumCertificate parses the given RawQuorumCertificate into a QuorumCertificate.
ParseQuorumCertificate(rawQC RawQuorumCertificate) (QuorumCertificate, error)
}

// QCDeserializerCreator creates a QCDeserializer from a list of NodePKs,
// which are used to verify the signatures in the QuorumCertificate.
type QCDeserializerCreator func(Nodes) QCDeserializer

// SignatureAggregator aggregates signatures into a QuorumCertificate
type SignatureAggregator interface {
// Aggregate aggregates several signatures into a QuorumCertificate
Expand Down Expand Up @@ -156,6 +163,7 @@ func (nws Nodes) NodeIDs() []NodeID {
type Node struct {
Node NodeID
Weight uint64
PK []byte
}

// SignatureAggregatorCreator creates a SignatureAggregator from a list of nodes and their weights.
Expand Down
207 changes: 201 additions & 6 deletions common/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import (
type Message struct {
BlockMessage *BlockMessage
VerifiedBlockMessage *VerifiedBlockMessage
EmptyNotarization *EmptyNotarization
EmptyNotarization *RawEmptyNotarization
VoteMessage *Vote
EmptyVoteMessage *EmptyVote
Notarization *Notarization
Notarization *RawNotarization
FinalizeVote *FinalizeVote
Finalization *Finalization
Finalization *RawFinalization
ReplicationResponse *ReplicationResponse
VerifiedReplicationResponse *VerifiedReplicationResponse
ReplicationRequest *ReplicationRequest
Expand Down Expand Up @@ -164,13 +164,28 @@ func (v *FinalizeVote) Signer() NodeID {
return v.Signature.Signer
}

// RawFinalization represents a finalization that contains a QuorumCertificate that has not been parsed yet.
// It is used to represent finalizations that are received from the network and have not been verified yet.
type RawFinalization struct {
Finalization ToBeSignedFinalization
QC RawQuorumCertificate
}

// Finalization represents a block that has reached quorum on block. This
// means that block can be included in the chain and finalized.
type Finalization struct {
Finalization ToBeSignedFinalization
QC QuorumCertificate
}

// Raw returns a RawFinalization with the same content but an unparsed QC.
func (f *Finalization) Raw() *RawFinalization {
return &RawFinalization{
Finalization: f.Finalization,
QC: f.QC,
}
}

func (f *Finalization) Verify() error {
context := "ToBeSignedFinalization"
return verifyContextQC(f.QC, f.Finalization.Bytes(), context)
Expand All @@ -187,6 +202,21 @@ func (n *Notarization) Verify() error {
return verifyContextQC(n.QC, n.Vote.Bytes(), context)
}

// Raw returns a RawNotarization with the same content but an unparsed QC.
func (n *Notarization) Raw() *RawNotarization {
return &RawNotarization{
Vote: n.Vote,
QC: n.QC,
}
}

// RawNotarization is a notarization that contains a QuorumCertificate that has not been parsed yet.
// It is used to represent notarizations that are received from the network and have not been verified yet.
type RawNotarization struct {
Vote ToBeSignedVote
QC RawQuorumCertificate
}

type BlockMessage struct {
Block Block
Vote Vote
Expand All @@ -197,11 +227,26 @@ type VerifiedBlockMessage struct {
Vote Vote
}

// RawEmptyNotarization is an empty notarization that contains a QuorumCertificate that has not been parsed yet.
// It is used to represent notarizations that are received from the network and have not been verified yet.
type RawEmptyNotarization struct {
Vote ToBeSignedEmptyVote
QC RawQuorumCertificate
}

type EmptyNotarization struct {
Vote ToBeSignedEmptyVote
QC QuorumCertificate
}

// Raw returns a RawEmptyNotarization with the same content but an unparsed QC.
func (en *EmptyNotarization) Raw() *RawEmptyNotarization {
return &RawEmptyNotarization{
Vote: en.Vote,
QC: en.QC,
}
}

func (en *EmptyNotarization) Verify() error {
context := "ToBeSignedEmptyVote"
return verifyContextQC(en.QC, en.Vote.Bytes(), context)
Expand All @@ -212,6 +257,14 @@ type SignedMessage struct {
Context string
}

// RawQuorumCertificate is an unparsed QuorumCertificate: it exposes its signers
// and raw bytes but cannot verify a message until it has been parsed into a
// QuorumCertificate via a QCDeserializer.
type RawQuorumCertificate interface {
Signers() []NodeID
Bytes() []byte
}

// QuorumCertificate is equivalent to a collection of signatures from a quorum of nodes,
type QuorumCertificate interface {
// Signers returns who participated in creating this QuorumCertificate.
Expand All @@ -231,9 +284,9 @@ type ReplicationRequest struct {
}

type ReplicationResponse struct {
Data []QuorumRound
LatestRound *QuorumRound
LatestSeq *QuorumRound
Data []RawQuorumRound
LatestRound *RawQuorumRound
LatestSeq *RawQuorumRound
}

type VerifiedReplicationResponse struct {
Expand All @@ -242,6 +295,113 @@ type VerifiedReplicationResponse struct {
LatestFinalizedSeq *VerifiedQuorumRound
}

// RawQuorumRound is the same as QuorumRound but with RawNotarization and RawFinalization instead of Notarization and Finalization.
// It is used to represent QuorumRounds that are received from the network and have not been verified yet.
type RawQuorumRound struct {
Block Block
Notarization *RawNotarization
Finalization *RawFinalization
EmptyNotarization *RawEmptyNotarization
}

// ToQuorumRound parses the raw QuorumCertificates contained in the round using
// the given QCDeserializer and returns the resulting QuorumRound. It returns an
// error if any of the QCs fail to parse.
func (rqr *RawQuorumRound) ToQuorumRound(qd QCDeserializer) (*QuorumRound, error) {
var notarization *Notarization
var finalization *Finalization
var emptyNotarization *EmptyNotarization
var err error

if rqr.Notarization != nil {
notarization = &Notarization{
Vote: rqr.Notarization.Vote,
}
notarization.QC, err = qd.ParseQuorumCertificate(rqr.Notarization.QC)
if err != nil {
return nil, fmt.Errorf("failed to parse notarization QC: %w", err)
}
}

if rqr.Finalization != nil {
finalization = &Finalization{
Finalization: rqr.Finalization.Finalization,
}
finalization.QC, err = qd.ParseQuorumCertificate(rqr.Finalization.QC)
if err != nil {
return nil, fmt.Errorf("failed to parse finalization QC: %w", err)
}
}

if rqr.EmptyNotarization != nil {
emptyNotarization = &EmptyNotarization{
Vote: rqr.EmptyNotarization.Vote,
}
emptyNotarization.QC, err = qd.ParseQuorumCertificate(rqr.EmptyNotarization.QC)
if err != nil {
return nil, fmt.Errorf("failed to parse empty notarization QC: %w", err)
}
}

return &QuorumRound{
Block: rqr.Block,
Notarization: notarization,
Finalization: finalization,
EmptyNotarization: emptyNotarization,
}, nil
}

// GetRound returns the round of the RawQuorumRound, or 0 if it cannot be determined.
func (rqr *RawQuorumRound) GetRound() uint64 {
if rqr.EmptyNotarization != nil {
return rqr.EmptyNotarization.Vote.Round
}

if rqr.Block != nil {
return rqr.Block.BlockHeader().Round
}

return 0
}

// GetSequence returns the sequence of the RawQuorumRound's block, or 0 if it has no block.
func (rqr *RawQuorumRound) GetSequence() uint64 {
if rqr.Block != nil {
return rqr.Block.BlockHeader().Seq
}

return 0
}

// String returns a string representation of the QuorumRound.
// It is meant as a debugging aid for logs.
func (rqr *RawQuorumRound) String() string {
if rqr != nil {
err := rqr.IsWellFormed()
if err != nil {
return fmt.Sprintf("QuorumRound{Error: %s}", err)
} else {
return fmt.Sprintf("QuorumRound{Round: %d, Seq: %d, Finalized: %t}", rqr.GetRound(), rqr.GetSequence(), rqr.Finalization != nil)
}
}

return "QuorumRound{nil}"
}

// IsWellFormed returns an error if the RawQuorumRound is not in one of these
// formats: (block, notarization), (block, finalization), or (empty notarization).
func (rqr *RawQuorumRound) IsWellFormed() error {
if rqr.Block == nil && rqr.EmptyNotarization == nil {
return fmt.Errorf("malformed QuorumRound, empty block and notarization fields")
}

if rqr.Block != nil && (rqr.Notarization == nil && rqr.Finalization == nil) {
return fmt.Errorf("malformed QuorumRound, block but no notarization or finalization")
}

return nil
}

// QuorumRound represents a round that has achieved quorum on either
// (empty notarization), (block & notarization), or (block, finalization)
type QuorumRound struct {
Expand All @@ -251,6 +411,41 @@ type QuorumRound struct {
EmptyNotarization *EmptyNotarization
}

// Raw returns a RawQuorumRound with the same content but unparsed QCs.
func (q *QuorumRound) Raw() *RawQuorumRound {
var rawNotarization *RawNotarization
var rawFinalization *RawFinalization
var rawEmptyNotarization *RawEmptyNotarization

if q.Notarization != nil {
rawNotarization = &RawNotarization{
Vote: q.Notarization.Vote,
QC: q.Notarization.QC,
}
}

if q.Finalization != nil {
rawFinalization = &RawFinalization{
Finalization: q.Finalization.Finalization,
QC: q.Finalization.QC,
}
}

if q.EmptyNotarization != nil {
rawEmptyNotarization = &RawEmptyNotarization{
Vote: q.EmptyNotarization.Vote,
QC: q.EmptyNotarization.QC,
}
}

return &RawQuorumRound{
Block: q.Block,
Notarization: rawNotarization,
Finalization: rawFinalization,
EmptyNotarization: rawEmptyNotarization,
}
}

// isWellFormed returns an error if the QuorumRound is not in one
// one of these formats
// (block, notarization), (block, finalization) or (empty notarization)
Expand Down
Loading
Loading