diff --git a/common/api.go b/common/api.go index 2c5b8839..18640963 100644 --- a/common/api.go +++ b/common/api.go @@ -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 @@ -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. diff --git a/common/msg.go b/common/msg.go index c54caa41..8e23c05b 100644 --- a/common/msg.go +++ b/common/msg.go @@ -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 @@ -164,6 +164,13 @@ 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 { @@ -171,6 +178,14 @@ type Finalization struct { 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) @@ -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 @@ -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) @@ -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. @@ -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 { @@ -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 { @@ -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) diff --git a/simplex/epoch.go b/simplex/epoch.go index b51d9d83..1684b1e8 100644 --- a/simplex/epoch.go +++ b/simplex/epoch.go @@ -64,7 +64,7 @@ type EpochConfig struct { MaxRoundWindow uint64 MaxRebroadcastWait time.Duration FinalizeRebroadcastTimeout time.Duration - QCDeserializer common.QCDeserializer + QCDeserializerCreator common.QCDeserializerCreator Logger common.Logger ID common.NodeID Signer common.Signer @@ -84,6 +84,7 @@ type EpochConfig struct { type Epoch struct { EpochConfig // Runtime + qcDeserializer common.QCDeserializer signatureAggregator common.SignatureAggregator oneTimeVerifier *OneTimeVerifier buildBlockScheduler *BasicScheduler @@ -168,13 +169,13 @@ func (e *Epoch) HandleMessage(msg *common.Message, from common.NodeID) error { case msg.EmptyVoteMessage != nil: return e.handleEmptyVoteMessage(msg.EmptyVoteMessage, from) case msg.Notarization != nil: - return e.handleNotarizationMessage(msg.Notarization, from) + return e.handleRawNotarizationMessage(msg.Notarization, from) case msg.EmptyNotarization != nil: - return e.handleEmptyNotarizationMessage(msg.EmptyNotarization, from) + return e.handleRawEmptyNotarizationMessage(msg.EmptyNotarization, from) case msg.FinalizeVote != nil: return e.handleFinalizeVoteMessage(msg.FinalizeVote, from) case msg.Finalization != nil: - return e.handleFinalizationMessage(msg.Finalization, from) + return e.handleRawFinalizationMessage(msg.Finalization, from) case msg.ReplicationResponse != nil && e.ReplicationEnabled: return e.handleReplicationResponse(msg.ReplicationResponse, from) case msg.ReplicationRequest != nil && e.ReplicationEnabled: @@ -213,6 +214,7 @@ func (e *Epoch) init() error { e.replicationState = NewReplicationState(e.Logger, e.Comm, e.ID, e.MaxRoundWindow, e.ReplicationEnabled, e.StartTime, &e.lock, e.RandomSource) e.timeoutHandler = NewTimeoutHandler(e.Logger, "emptyVoteRebroadcast", e.StartTime, e.MaxRebroadcastWait, e.emptyVoteTimeoutTaskRunner) e.signatureAggregator = e.SignatureAggregatorCreator(e.nodes) + e.qcDeserializer = e.QCDeserializerCreator(e.nodes) for _, node := range e.nodeIDs { e.futureMessages[string(node)] = make(map[uint64]*messagesForRound) @@ -333,7 +335,7 @@ func (e *Epoch) loadBlockRecord(block common.Block) error { } func (e *Epoch) restoreNotarizationRecord(r []byte, highestWalRound *walRound) error { - notarization, err := common.NotarizationFromRecord(r, e.QCDeserializer) + notarization, err := common.NotarizationFromRecord(r, e.qcDeserializer) if err != nil { return err } @@ -352,7 +354,7 @@ func (e *Epoch) restoreNotarizationRecord(r []byte, highestWalRound *walRound) e } func (e *Epoch) loadNotarizationRecord(r []byte) error { - notarization, err := common.NotarizationFromRecord(r, e.QCDeserializer) + notarization, err := common.NotarizationFromRecord(r, e.qcDeserializer) if err != nil { return err } @@ -372,7 +374,7 @@ func (e *Epoch) loadNotarizationRecord(r []byte) error { } func (e *Epoch) restoreEmptyNotarizationRecord(r []byte, highestWalRound *walRound) error { - emptyNotarization, err := common.EmptyNotarizationFromRecord(r, e.QCDeserializer) + emptyNotarization, err := common.EmptyNotarizationFromRecord(r, e.qcDeserializer) if err != nil { return err } @@ -391,7 +393,7 @@ func (e *Epoch) restoreEmptyNotarizationRecord(r []byte, highestWalRound *walRou } func (e *Epoch) loadEmptyNotarizationRecord(r []byte) error { - emptyNotarization, err := common.EmptyNotarizationFromRecord(r, e.QCDeserializer) + emptyNotarization, err := common.EmptyNotarizationFromRecord(r, e.qcDeserializer) if err != nil { return err } @@ -448,7 +450,7 @@ func (e *Epoch) loadEmptyVoteRecord(r []byte) error { } func (e *Epoch) restoreFinalizationRecord(r []byte, highestWalRound *walRound) error { - finalization, err := common.FinalizationFromRecord(r, e.QCDeserializer) + finalization, err := common.FinalizationFromRecord(r, e.qcDeserializer) if err != nil { return err } @@ -467,7 +469,7 @@ func (e *Epoch) restoreFinalizationRecord(r []byte, highestWalRound *walRound) e } func (e *Epoch) loadFinalizationRecord(r []byte) error { - finalization, err := common.FinalizationFromRecord(r, e.QCDeserializer) + finalization, err := common.FinalizationFromRecord(r, e.qcDeserializer) if err != nil { return err } @@ -519,7 +521,7 @@ func (e *Epoch) resumeFromWal(highestRoundRecord *walRound) error { // Handle the most relevant record based on priority: finalization > notarization > emptyNotarization > emptyVote > block if highestRoundRecord.finalization != nil { - finalizationMsg := &common.Message{Finalization: highestRoundRecord.finalization} + finalizationMsg := &common.Message{Finalization: highestRoundRecord.finalization.Raw()} e.Comm.Broadcast(finalizationMsg) e.Logger.Debug("Broadcast finalization", @@ -531,7 +533,7 @@ func (e *Epoch) resumeFromWal(highestRoundRecord *walRound) error { if highestRoundRecord.notarization != nil { notarization := highestRoundRecord.notarization - lastMessage := common.Message{Notarization: notarization} + lastMessage := common.Message{Notarization: notarization.Raw()} e.Comm.Broadcast(&lastMessage) if e.sequenceAlreadyIndexed(notarization.Vote.Seq) { @@ -543,7 +545,7 @@ func (e *Epoch) resumeFromWal(highestRoundRecord *walRound) error { } if highestRoundRecord.emptyNotarization != nil { - lastMessage := common.Message{EmptyNotarization: highestRoundRecord.emptyNotarization} + lastMessage := common.Message{EmptyNotarization: highestRoundRecord.emptyNotarization.Raw()} e.Comm.Broadcast(&lastMessage) return e.startRound() } @@ -624,7 +626,7 @@ func (e *Epoch) setMetadataFromRecords(records [][]byte) error { recordType := binary.BigEndian.Uint16(records[i]) switch recordType { case record.NotarizationRecordType: - notarization, err := common.NotarizationFromRecord(records[i], e.QCDeserializer) + notarization, err := common.NotarizationFromRecord(records[i], e.qcDeserializer) if err != nil { return err } @@ -634,7 +636,7 @@ func (e *Epoch) setMetadataFromRecords(records [][]byte) error { found = true } case record.EmptyNotarizationRecordType: - emptyNotarization, err := common.EmptyNotarizationFromRecord(records[i], e.QCDeserializer) + emptyNotarization, err := common.EmptyNotarizationFromRecord(records[i], e.qcDeserializer) if err != nil { return err } @@ -644,7 +646,7 @@ func (e *Epoch) setMetadataFromRecords(records [][]byte) error { found = true } case record.FinalizationRecordType: - finalization, err := common.FinalizationFromRecord(records[i], e.QCDeserializer) + finalization, err := common.FinalizationFromRecord(records[i], e.qcDeserializer) if err != nil { return err } @@ -734,6 +736,19 @@ func (e *Epoch) Stop() { e.replicationState.Close() } +func (e *Epoch) handleRawFinalizationMessage(message *common.RawFinalization, from common.NodeID) error { + qc, err := e.qcDeserializer.ParseQuorumCertificate(message.QC) + if err != nil { + e.Logger.Debug("Failed to parse QC in raw finalization message", zap.Error(err)) + return nil + } + finalization := &common.Finalization{ + QC: qc, + Finalization: message.Finalization, + } + return e.handleFinalizationMessage(finalization, from) +} + func (e *Epoch) handleFinalizationMessage(message *common.Finalization, from common.NodeID) error { e.Logger.Verbo("Received finalization message", zap.Stringer("from", from), zap.Uint64("round", message.Finalization.Round), zap.Uint64("seq", message.Finalization.Seq)) @@ -840,7 +855,7 @@ func (e *Epoch) handleFinalizeVoteMessage(message *common.FinalizeVote, from com } // send the finalization to the sender in case they missed it e.Comm.Send(&common.Message{ - Finalization: round.finalization, + Finalization: round.finalization.Raw(), }, from) return nil } @@ -936,7 +951,7 @@ func (e *Epoch) sendLatestFinalization(to common.NodeID) { } msg := &common.Message{ - Finalization: &e.lastBlock.Finalization, + Finalization: e.lastBlock.Finalization.Raw(), } e.Logger.Debug("Node appears behind, sending it the latest finalization", zap.Stringer("to", to), zap.Uint64("round", e.lastBlock.Finalization.Finalization.Round), zap.Uint64("sequence", e.lastBlock.Finalization.Finalization.Seq)) e.Comm.Send(msg, to) @@ -952,7 +967,7 @@ func (e *Epoch) sendHighestRound(to common.NodeID) { if latestQR.Notarization != nil { msg := &common.Message{ - Notarization: latestQR.Notarization, + Notarization: latestQR.Notarization.Raw(), } e.Logger.Debug("Node appears behind, sending it the highest round", zap.Stringer("to", to), zap.Uint64("round", latestQR.Notarization.Vote.Round)) e.Comm.Send(msg, to) @@ -961,7 +976,7 @@ func (e *Epoch) sendHighestRound(to common.NodeID) { if latestQR.EmptyNotarization != nil { msg := &common.Message{ - EmptyNotarization: latestQR.EmptyNotarization, + EmptyNotarization: latestQR.EmptyNotarization.Raw(), } e.Logger.Debug("Node appears behind, sending it the highest empty notarized round", zap.Stringer("to", to), zap.Uint64("round", latestQR.EmptyNotarization.Vote.Round)) e.Comm.Send(msg, to) @@ -977,7 +992,7 @@ func (e *Epoch) maybeSendNotarizationOrFinalization(to common.NodeID, round uint evs, ok := e.emptyVotes[round] if ok && evs.emptyNotarization != nil { msg := &common.Message{ - EmptyNotarization: evs.emptyNotarization, + EmptyNotarization: evs.emptyNotarization.Raw(), } e.Logger.Debug("Node appears behind, sending it an empty notarization", zap.Stringer("to", to), zap.Uint64("round", round)) e.Comm.Send(msg, to) @@ -988,7 +1003,7 @@ func (e *Epoch) maybeSendNotarizationOrFinalization(to common.NodeID, round uint if r.finalization != nil { msg := &common.Message{ - Finalization: r.finalization, + Finalization: r.finalization.Raw(), } e.Comm.Send(msg, to) return @@ -997,7 +1012,7 @@ func (e *Epoch) maybeSendNotarizationOrFinalization(to common.NodeID, round uint if r.notarization != nil { e.Logger.Debug("Node appears behind, sending it a notarization", zap.Stringer("to", to), zap.Uint64("round", round)) msg := &common.Message{ - Notarization: r.notarization, + Notarization: r.notarization.Raw(), } e.Comm.Send(msg, to) return @@ -1248,7 +1263,7 @@ func (e *Epoch) persistFinalization(finalization common.Finalization) error { } } - finalizationMsg := &common.Message{Finalization: &finalization} + finalizationMsg := &common.Message{Finalization: finalization.Raw()} e.Comm.Broadcast(finalizationMsg) e.Logger.Debug("Broadcast finalization", @@ -1452,7 +1467,7 @@ func (e *Epoch) persistEmptyNotarization(emptyVotes *EmptyVoteSet, shouldBroadca emptyVotes.persisted = true if shouldBroadcast { - notarizationMessage := &common.Message{EmptyNotarization: emptyNotarization} + notarizationMessage := &common.Message{EmptyNotarization: emptyNotarization.Raw()} e.Comm.Broadcast(notarizationMessage) e.Logger.Debug("Broadcast empty notarization", zap.Uint64("round", emptyNotarization.Vote.Round)) @@ -1579,7 +1594,7 @@ func (e *Epoch) persistAndBroadcastNotarization(notarization common.Notarization return err } - notarizationMessage := &common.Message{Notarization: ¬arization} + notarizationMessage := &common.Message{Notarization: notarization.Raw()} e.Comm.Broadcast(notarizationMessage) e.Logger.Debug("Broadcast notarization", @@ -1590,6 +1605,19 @@ func (e *Epoch) persistAndBroadcastNotarization(notarization common.Notarization return e.doNotarized(notarization.Vote.Round) } +func (e *Epoch) handleRawEmptyNotarizationMessage(message *common.RawEmptyNotarization, from common.NodeID) error { + qc, err := e.qcDeserializer.ParseQuorumCertificate(message.QC) + if err != nil { + e.Logger.Debug("Failed to parse QC in raw empty notarization message", zap.Error(err)) + return nil + } + emptyNotarization := &common.EmptyNotarization{ + QC: qc, + Vote: message.Vote, + } + return e.handleEmptyNotarizationMessage(emptyNotarization, from) +} + func (e *Epoch) handleEmptyNotarizationMessage(emptyNotarization *common.EmptyNotarization, from common.NodeID) error { vote := emptyNotarization.Vote @@ -1649,6 +1677,19 @@ func (e *Epoch) isVoteForFinalizedRound(round uint64) bool { return round < max } +func (e *Epoch) handleRawNotarizationMessage(message *common.RawNotarization, from common.NodeID) error { + qc, err := e.qcDeserializer.ParseQuorumCertificate(message.QC) + if err != nil { + e.Logger.Debug("Failed to parse QC in raw notarization message", zap.Error(err)) + return nil + } + notarization := &common.Notarization{ + QC: qc, + Vote: message.Vote, + } + return e.handleNotarizationMessage(notarization, from) +} + func (e *Epoch) handleNotarizationMessage(message *common.Notarization, from common.NodeID) error { vote := message.Vote @@ -3188,17 +3229,33 @@ func (e *Epoch) handleReplicationResponse(resp *common.ReplicationResponse, from continue } - if err := e.processQuorumRound(&data, from); err != nil { + qr, err := data.ToQuorumRound(e.qcDeserializer) + if err != nil { + e.Logger.Debug("Failed deserializing quorum round", zap.Error(err)) + continue + } + + if err := e.processQuorumRound(qr, from); err != nil { e.Logger.Debug("Failed processing quorum round", zap.Error(err)) } } - if err := e.processQuorumRound(resp.LatestRound, from); err != nil { - e.Logger.Debug("Failed processing latest round", zap.Error(err)) + latestRound, err := resp.LatestRound.ToQuorumRound(e.qcDeserializer) + if err != nil { + e.Logger.Debug("Failed deserializing latest round", zap.Error(err)) + } else { + if err := e.processQuorumRound(latestRound, from); err != nil { + e.Logger.Debug("Failed processing latest round", zap.Error(err)) + } } - if err := e.processQuorumRound(resp.LatestSeq, from); err != nil { - e.Logger.Debug("Failed processing latest seq", zap.Error(err)) + latestSeq, err := resp.LatestSeq.ToQuorumRound(e.qcDeserializer) + if err != nil { + e.Logger.Debug("Failed deserializing latest seq", zap.Error(err)) + } else { + if err := e.processQuorumRound(latestSeq, from); err != nil { + e.Logger.Debug("Failed processing latest seq", zap.Error(err)) + } } return e.processReplicationState() diff --git a/simplex/epoch_failover_test.go b/simplex/epoch_failover_test.go index f8489ed2..e5854647 100644 --- a/simplex/epoch_failover_test.go +++ b/simplex/epoch_failover_test.go @@ -65,7 +65,7 @@ func TestEpochLeaderFailoverWithEmptyNotarization(t *testing.T) { emptyNotarization := testutil.NewEmptyNotarization(nodes[:3], 2) e.HandleMessage(&Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[1]) notarizeAndFinalizeRoundWithMetadata(t, e, bb, &block1.Metadata) @@ -181,7 +181,7 @@ func TestEpochLeaderFailoverReceivesEmptyVotesEarly(t *testing.T) { require.NoError(t, err) require.Equal(t, createEmptyVote(emptyBlockMd, nodes[0]).Vote, emptyVote) - emptyNotarization, err := EmptyNotarizationFromRecord(rawEmptyNotarization, e.QCDeserializer) + emptyNotarization, err := EmptyNotarizationFromRecord(rawEmptyNotarization, e.QCDeserializerCreator(nil)) require.NoError(t, err) require.Equal(t, emptyVoteFrom1.Vote, emptyNotarization.Vote) require.Equal(t, uint64(3), emptyNotarization.Vote.Round) @@ -220,7 +220,7 @@ func TestReceiveEmptyNotarizationWithNoQC(t *testing.T) { emptyNotarization := testutil.NewEmptyNotarization(nodes[:3], 0) e.HandleMessage(&Message{ - EmptyNotarization: &EmptyNotarization{Vote: emptyNotarization.Vote}, + EmptyNotarization: &RawEmptyNotarization{Vote: emptyNotarization.Vote, QC: emptyNotarization.QC}, }, nodes[0]) } @@ -272,7 +272,7 @@ func TestEpochLeaderFailover(t *testing.T) { require.NoError(t, err) require.Equal(t, createEmptyVote(emptyBlockMd, nodes[0]).Vote, emptyVote) - emptyNotarization, err := EmptyNotarizationFromRecord(rawEmptyNotarization, e.QCDeserializer) + emptyNotarization, err := EmptyNotarizationFromRecord(rawEmptyNotarization, e.QCDeserializerCreator(nil)) require.NoError(t, err) require.Equal(t, emptyVoteFrom1.Vote, emptyNotarization.Vote) require.Equal(t, uint64(3), emptyNotarization.Vote.Round) @@ -336,7 +336,7 @@ func TestEpochLeaderFailoverDoNotPersistEmptyRoundTwice(t *testing.T) { en := testutil.NewEmptyNotarization(nodes, numRounds) err = e.HandleMessage(&Message{ - EmptyNotarization: en, + EmptyNotarization: en.Raw(), }, nodes[2]) require.NoError(t, err) @@ -380,7 +380,7 @@ func TestEpochLeaderRecursivelyFetchNotarizedBlocks(t *testing.T) { testutil.WaitForBlockProposerTimeout(t, e, &startTime, round) err = e.HandleMessage(&Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[2]) require.NoError(t, err) @@ -391,7 +391,7 @@ func TestEpochLeaderRecursivelyFetchNotarizedBlocks(t *testing.T) { // Otherwise, just receive the empty notarization // and advance to the next round err = e.HandleMessage(&Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[2]) require.NoError(t, err) wal.AssertNotarization(round) @@ -531,6 +531,7 @@ func TestEpochLeaderFailoverAfterProposal(t *testing.T) { nodes := []NodeID{{1}, {2}, {3}, {4}} conf, wal, storage := testutil.DefaultTestNodeEpochConfig(t, nodes[0], testutil.NewNoopComm(nodes), bb) + qcDeserializer := conf.QCDeserializerCreator(nil) e, err := NewEpoch(conf) require.NoError(t, err) @@ -608,7 +609,7 @@ func TestEpochLeaderFailoverAfterProposal(t *testing.T) { require.NoError(t, err) require.Equal(t, createEmptyVote(md, nodes[0]).Vote, emptyVote) - emptyNotarization, err := EmptyNotarizationFromRecord(rawEmptyNotarization, e.QCDeserializer) + emptyNotarization, err := EmptyNotarizationFromRecord(rawEmptyNotarization, qcDeserializer) require.NoError(t, err) require.Equal(t, emptyVoteFrom1.Vote, emptyNotarization.Vote) require.Equal(t, uint64(3), emptyNotarization.Vote.Round) @@ -696,7 +697,7 @@ func TestEpochLeaderFailoverTwice(t *testing.T) { require.NoError(t, err) require.Equal(t, createEmptyVote(md, nodes[0]).Vote, emptyVote) - emptyNotarization, err := EmptyNotarizationFromRecord(rawEmptyNotarization, e.QCDeserializer) + emptyNotarization, err := EmptyNotarizationFromRecord(rawEmptyNotarization, e.QCDeserializerCreator(nil)) require.NoError(t, err) require.Equal(t, emptyVoteFrom1.Vote, emptyNotarization.Vote) require.Equal(t, uint64(3), emptyNotarization.Vote.Round) @@ -727,7 +728,7 @@ func TestEpochLeaderFailoverGarbageCollectedEmptyVotes(t *testing.T) { if strings.Contains(entry.Message, "It is time to build a block") { emptyNotarization := testutil.NewEmptyNotarization(nodes[1:], 3) e.HandleMessage(&Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[1]) waitForTimeoutOnce.Do(waitForTimeout.Done) @@ -942,7 +943,7 @@ func TestEpochBlacklist(t *testing.T) { // Forcefully time out the last node. err = e.HandleMessage(&Message{ - EmptyNotarization: testutil.NewEmptyNotarization(nodes, timedOutRound), + EmptyNotarization: testutil.NewEmptyNotarization(nodes, timedOutRound).Raw(), }, nodes[1]) require.NoError(t, err) @@ -1171,7 +1172,7 @@ func TestEpochRebroadcastsEmptyVote(t *testing.T) { emptyNotarization := testutil.NewEmptyNotarization(nodes, 0) e.HandleMessage(&Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[2]) wal.AssertNotarization(0) diff --git a/simplex/epoch_test.go b/simplex/epoch_test.go index 974022c6..0b5c57b9 100644 --- a/simplex/epoch_test.go +++ b/simplex/epoch_test.go @@ -113,7 +113,7 @@ func TestFinalizeSameSequence(t *testing.T) { // now lets send empty notarization emptyNotarization := testutil.NewEmptyNotarization(nodes, 1) err = e.HandleMessage(&Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[3]) require.NoError(t, err) @@ -263,7 +263,7 @@ func testFinalizeSameSequenceGap(t *testing.T, nodes []NodeID, numEmptyNotarizat for i := startMissingNotarizationRound; i < md.Round; i++ { emptyNotarization := testutil.NewEmptyNotarization(nodes, i) err = e.HandleMessage(&Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[3]) require.NoError(t, err) } @@ -352,7 +352,7 @@ func TestBlockNotVerifiedIfParentNotNotarized(t *testing.T) { require.NoError(t, err) err = e.HandleMessage(&Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[1]) require.NoError(t, err) @@ -385,7 +385,7 @@ func TestEpochHandleNotarizationFutureRound(t *testing.T) { // Give the node the notarization message before receiving the first block e.HandleMessage(&Message{ - Notarization: ¬arization, + Notarization: notarization.Raw(), }, nodes[1]) // Run through round 0 @@ -579,7 +579,7 @@ func TestEpochNotarizeTwiceThenFinalize(t *testing.T) { // Round 1 emptyNote := testutil.NewEmptyNotarization(nodes, 1) err = e.HandleMessage(&Message{ - EmptyNotarization: emptyNote, + EmptyNotarization: emptyNote.Raw(), }, nodes[1]) require.NoError(t, err) emptyRecord := wal.AssertNotarization(1) @@ -754,7 +754,7 @@ func advanceRoundFromEmpty(t *testing.T, e *Epoch) { emptyNote := testutil.NewEmptyNotarization(e.Comm.Nodes().NodeIDs(), e.Metadata().Round) err := e.HandleMessage(&Message{ - EmptyNotarization: emptyNote, + EmptyNotarization: emptyNote.Raw(), }, leader) require.NoError(t, err) @@ -1020,7 +1020,7 @@ func TestEpochQCSignedByNonExistentNodes(t *testing.T) { require.NoError(t, err) err = e.HandleMessage(&Message{ - Notarization: ¬arization, + Notarization: notarization.Raw(), }, nodes[1]) require.NoError(t, err) @@ -1038,7 +1038,7 @@ func TestEpochQCSignedByNonExistentNodes(t *testing.T) { notarization.QC = tqc err = e.HandleMessage(&Message{ - Notarization: ¬arization, + Notarization: notarization.Raw(), }, nodes[1]) require.NoError(t, err) @@ -1052,7 +1052,7 @@ func TestEpochQCSignedByNonExistentNodes(t *testing.T) { } err = e.HandleMessage(&Message{ - EmptyNotarization: &EmptyNotarization{ + EmptyNotarization: &RawEmptyNotarization{ Vote: ToBeSignedEmptyVote{EmptyVoteMetadata: EmptyVoteMetadata{ Round: 0, Epoch: 0, @@ -1072,7 +1072,7 @@ func TestEpochQCSignedByNonExistentNodes(t *testing.T) { } err = e.HandleMessage(&Message{ - EmptyNotarization: &EmptyNotarization{ + EmptyNotarization: &RawEmptyNotarization{ Vote: ToBeSignedEmptyVote{EmptyVoteMetadata: EmptyVoteMetadata{ Round: 0, Epoch: 0, @@ -1089,7 +1089,7 @@ func TestEpochQCSignedByNonExistentNodes(t *testing.T) { finalization, _ := testutil.NewFinalizationRecord(t, sigAggr, block, []NodeID{{2}, {3}, {5}}) err = e.HandleMessage(&Message{ - Finalization: &finalization, + Finalization: finalization.Raw(), }, nodes[1]) require.NoError(t, err) @@ -1100,7 +1100,7 @@ func TestEpochQCSignedByNonExistentNodes(t *testing.T) { finalization, _ := testutil.NewFinalizationRecord(t, sigAggr, block, []NodeID{{2}, {3}, {3}}) err = e.HandleMessage(&Message{ - Finalization: &finalization, + Finalization: finalization.Raw(), }, nodes[1]) require.NoError(t, err) @@ -1243,7 +1243,7 @@ func TestEpochSendsBlockDigestRequest(t *testing.T) { round0Empty := testutil.NewEmptyNotarization(nodes, 0) err = e.HandleMessage(&Message{ - EmptyNotarization: round0Empty, + EmptyNotarization: round0Empty.Raw(), }, nodes[0]) require.NoError(t, err) @@ -1260,7 +1260,7 @@ func TestEpochSendsBlockDigestRequest(t *testing.T) { require.NoError(t, err) err = e.HandleMessage(&Message{ - Notarization: ¬arization, + Notarization: notarization.Raw(), }, nodes[1]) require.NoError(t, err) @@ -1275,10 +1275,10 @@ func TestEpochSendsBlockDigestRequest(t *testing.T) { // send the response with the block replicationResponse := &ReplicationResponse{ - Data: []QuorumRound{ + Data: []RawQuorumRound{ { Block: block, - Notarization: ¬arization, + Notarization: notarization.Raw(), }, }, } @@ -1375,7 +1375,7 @@ func TestEpochVotesForEquivocatedVotes(t *testing.T) { // Wait for the notarization to be sent timeout := time.After(time.Minute) - var notarization *Notarization + var notarization *RawNotarization for notarization == nil { select { case msg := <-recordedMessages: @@ -1445,9 +1445,9 @@ func TestEpochRequestsEmptyRoundDependency(t *testing.T) { // send the response with the block replicationResponse := &ReplicationResponse{ - Data: []QuorumRound{ + Data: []RawQuorumRound{ { - EmptyNotarization: missingEmptyNotarization, + EmptyNotarization: missingEmptyNotarization.Raw(), }, }, } @@ -1507,10 +1507,10 @@ func TestDoubleIncrementOnPersistNotarization(t *testing.T) { err = e.HandleMessage(&Message{ ReplicationResponse: &ReplicationResponse{ - Data: []QuorumRound{ + Data: []RawQuorumRound{ { Block: block, - Notarization: ¬arization, + Notarization: notarization.Raw(), }, }, }, @@ -1547,7 +1547,7 @@ func TestRejectsOldNotarizationAndVotes(t *testing.T) { initialBlock := createBlocks(t, nodes, 1)[0] conf, wal, storage := testutil.DefaultTestNodeEpochConfig(t, nodes[3], testutil.NewNoopComm(nodes), bb) storage.Index(ctx, initialBlock.VerifiedBlock, initialBlock.Finalization) - + qcDeserializer := conf.QCDeserializerCreator(nil) e, err := NewEpoch(conf) require.NoError(t, err) t.Cleanup(e.Stop) @@ -1598,7 +1598,7 @@ func TestRejectsOldNotarizationAndVotes(t *testing.T) { require.NoError(t, err) err = e.HandleMessage(&Message{ - Notarization: ¬arization, + Notarization: notarization.Raw(), }, nodes[0]) require.NoError(t, err) @@ -1613,7 +1613,7 @@ func TestRejectsOldNotarizationAndVotes(t *testing.T) { if len(bb.BlockShouldBeBuilt) == 0 { bb.BlockShouldBeBuilt <- struct{}{} } - wal.AssertHealthy(e.BlockDeserializer, e.QCDeserializer) + wal.AssertHealthy(e.BlockDeserializer, qcDeserializer) e.AdvanceTime(e.StartTime.Add(conf.MaxProposalWait * time.Duration(increment))) time.Sleep(100 * time.Millisecond) increment++ diff --git a/simplex/recovery_test.go b/simplex/recovery_test.go index e32f7da7..85f3c3f8 100644 --- a/simplex/recovery_test.go +++ b/simplex/recovery_test.go @@ -401,7 +401,7 @@ func TestWalWritesFinalization(t *testing.T) { require.Len(t, records, 5) recordType := binary.BigEndian.Uint16(records[4]) require.Equal(t, record.FinalizationRecordType, recordType) - _, err = FinalizationFromRecord(records[4], e.QCDeserializer) + _, err = FinalizationFromRecord(records[4], e.QCDeserializerCreator(nil)) _, expectedFinalizationRecord := testutil.NewFinalizationRecord(t, sigAggregrator, secondBlock, nodes[0:quorum]) require.NoError(t, err) require.Equal(t, expectedFinalizationRecord, records[4]) @@ -465,7 +465,7 @@ func TestRecoverFromMultipleNotarizations(t *testing.T) { // now if we send finalization for block 1, we should index both 1 & 2 finalization1, _ := testutil.NewFinalizationRecord(t, sigAggr, firstBlock, nodes[0:quorum]) err = e.HandleMessage(&Message{ - Finalization: &finalization1, + Finalization: finalization1.Raw(), }, nodes[1]) require.NoError(t, err) diff --git a/simplex/replication_request_test.go b/simplex/replication_request_test.go index 54ac1765..cdb4e775 100644 --- a/simplex/replication_request_test.go +++ b/simplex/replication_request_test.go @@ -173,7 +173,7 @@ func TestReplicationRequestMixed(t *testing.T) { if emptyBlock { emptyNotarization := testutil.NewEmptyNotarization(nodes, uint64(i)) e.HandleMessage(&common.Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[1]) wal.AssertNotarization(uint64(i)) rounds[i] = common.VerifiedQuorumRound{ @@ -238,7 +238,7 @@ func TestReplicationRequestTailingEmptyNotarizations(t *testing.T) { for i := range numBlocks { emptyNotarization := testutil.NewEmptyNotarization(nodes, uint64(i)) e.HandleMessage(&common.Message{ - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, nodes[1]) wal.AssertNotarization(uint64(i)) rounds[i] = common.VerifiedQuorumRound{ @@ -312,7 +312,7 @@ func TestNilReplicationResponse(t *testing.T) { err := normalNode0.HandleMessage(&common.Message{ ReplicationResponse: &common.ReplicationResponse{ - Data: []common.QuorumRound{{}}, + Data: []common.RawQuorumRound{{}}, }, }, nodes[1]) require.NoError(t, err) @@ -331,7 +331,7 @@ func TestMalformedReplicationResponse(t *testing.T) { err := normalNode0.HandleMessage(&common.Message{ ReplicationResponse: &common.ReplicationResponse{ - Data: []common.QuorumRound{{ + Data: []common.RawQuorumRound{{ Block: &testutil.TestBlock{}, }}, }, diff --git a/simplex/replication_test.go b/simplex/replication_test.go index e4e810dd..f58cd588 100644 --- a/simplex/replication_test.go +++ b/simplex/replication_test.go @@ -136,7 +136,7 @@ func TestReplicationAdversarialNode(t *testing.T) { sigAggr := laggingNode.E.SignatureAggregatorCreator(laggingNode.E.Comm.Nodes()) finalization, _ := NewFinalizationRecord(t, sigAggr, blocks[1], nodes[:quorum]) finalizationMsg := &common.Message{ - Finalization: &finalization, + Finalization: finalization.Raw(), } laggingNode.E.HandleMessage(finalizationMsg, doubleBlockProposalNode.E.ID) @@ -448,7 +448,7 @@ func TestReplicationFutureFinalization(t *testing.T) { finalization, _ := NewFinalizationRecord(t, sigAggr, block, nodes[0:quorum]) // send finalization err = e.HandleMessage(&common.Message{ - Finalization: &finalization, + Finalization: finalization.Raw(), }, nodes[0]) require.NoError(t, err) @@ -636,7 +636,7 @@ func TestReplicationStuckInProposingBlock(t *testing.T) { // Trigger the replication process to start by sending a finalization for a block we do not have e.HandleMessage(&common.Message{ - Finalization: &highFinalization, + Finalization: highFinalization.Raw(), }, nodes[1]) // Wait for the replication request to be sent @@ -656,13 +656,13 @@ func TestReplicationStuckInProposingBlock(t *testing.T) { } // Prepare the quorum round answer to be sent as a response to the replication request - quorumRounds := make([]common.QuorumRound, 0, 4) + quorumRounds := make([]common.RawQuorumRound, 0, 4) for i := uint64(1); i <= 4; i++ { tb := blocks[i].VerifiedBlock.(*TestBlock) finalization := blocks[i].Finalization - quorumRounds = append(quorumRounds, common.QuorumRound{ + quorumRounds = append(quorumRounds, common.RawQuorumRound{ Block: tb, - Finalization: &finalization, + Finalization: finalization.Raw(), }) } @@ -681,7 +681,7 @@ func TestReplicationStuckInProposingBlock(t *testing.T) { // Trigger the replication process to start by sending a finalization for a block we do not have e.HandleMessage(&common.Message{ - Finalization: &blocks[4].Finalization, + Finalization: blocks[4].Finalization.Raw(), }, nodes[1]) // Wait for the replication request to be sent @@ -982,7 +982,7 @@ func TestReplicationVerifyNotarization(t *testing.T) { // Trigger the replication process to start by sending a finalization for a block we do not have e.HandleMessage(&common.Message{ - Finalization: &finalization, + Finalization: finalization.Raw(), }, nodes[0]) // Wait for the replication request to be sent @@ -1001,10 +1001,10 @@ func TestReplicationVerifyNotarization(t *testing.T) { // Respond to the replication request with a block that has a notarization replicationResponse := &common.ReplicationResponse{ - Data: []common.QuorumRound{ + Data: []common.RawQuorumRound{ { Block: block, - Notarization: ¬arization, + Notarization: notarization.Raw(), }, }, } @@ -1070,7 +1070,7 @@ func TestReplicationVerifyEmptyNotarization(t *testing.T) { // Trigger the replication process to start by sending a finalization for a block we do not have e.HandleMessage(&common.Message{ - Finalization: &finalization, + Finalization: finalization.Raw(), }, nodes[0]) // Wait for the replication request to be sent @@ -1088,9 +1088,9 @@ func TestReplicationVerifyEmptyNotarization(t *testing.T) { // Respond to the replication request with a block that has a notarization replicationResponse := &common.ReplicationResponse{ - Data: []common.QuorumRound{ + Data: []common.RawQuorumRound{ { - EmptyNotarization: emptyNotarization, + EmptyNotarization: emptyNotarization.Raw(), }, }, } @@ -1379,7 +1379,7 @@ func TestReplicationStoresFinalization(t *testing.T) { // Send a finalization for block 2 to trigger replication finalization := blocks[1].Finalization e.HandleMessage(&common.Message{ - Finalization: &finalization, + Finalization: finalization.Raw(), }, nodes[1]) // Wait for the replication request to be sent @@ -1391,14 +1391,14 @@ func TestReplicationStoresFinalization(t *testing.T) { } // pass in replication response to epoch with both blocks and their finalizations - quorumRounds := []common.QuorumRound{ + quorumRounds := []common.RawQuorumRound{ { Block: blocks[0].VerifiedBlock.(common.Block), - Finalization: &blocks[0].Finalization, + Finalization: blocks[0].Finalization.Raw(), }, { Block: blocks[1].VerifiedBlock.(common.Block), - Finalization: &blocks[1].Finalization, + Finalization: blocks[1].Finalization.Raw(), }, } @@ -1556,7 +1556,7 @@ func TestReplicationStartsRoundFromFinalization(t *testing.T) { lastFinalization := blocks[len(blocks)-1].Finalization e.HandleMessage(&common.Message{ - Finalization: &lastFinalization, + Finalization: lastFinalization.Raw(), }, nodes[1]) // Wait for the replication request to be sent @@ -1568,12 +1568,12 @@ func TestReplicationStartsRoundFromFinalization(t *testing.T) { } // Prepare replication response with all the blocks - quorumRounds := make([]common.QuorumRound, 0, len(blocks)) + quorumRounds := make([]common.RawQuorumRound, 0, len(blocks)) for _, vfb := range blocks { tb := vfb.VerifiedBlock.(*TestBlock) - quorumRounds = append(quorumRounds, common.QuorumRound{ + quorumRounds = append(quorumRounds, common.RawQuorumRound{ Block: tb, - Finalization: &vfb.Finalization, + Finalization: vfb.Finalization.Raw(), }) } @@ -1677,12 +1677,12 @@ func TestReplicationStartsRoundFromFinalizationWithBlock(t *testing.T) { require.NoError(t, err) // Prepare replication response with all the blocks - quorumRounds := make([]common.QuorumRound, 0, len(blocks)) + quorumRounds := make([]common.RawQuorumRound, 0, len(blocks)) for _, vfb := range blocks { tb := vfb.VerifiedBlock.(*TestBlock) - quorumRounds = append(quorumRounds, common.QuorumRound{ + quorumRounds = append(quorumRounds, common.RawQuorumRound{ Block: tb, - Finalization: &vfb.Finalization, + Finalization: vfb.Finalization.Raw(), }) } @@ -1705,7 +1705,7 @@ func TestReplicationStartsRoundFromFinalizationWithBlock(t *testing.T) { // Send replication response replicationResponse = &common.ReplicationResponse{ - Data: []common.QuorumRound{quorumRounds[len(quorumRounds)-1]}, + Data: []common.RawQuorumRound{quorumRounds[len(quorumRounds)-1]}, } e.HandleMessage(&common.Message{ diff --git a/simplex/replication_timeout_test.go b/simplex/replication_timeout_test.go index f234a7cb..0d6d419f 100644 --- a/simplex/replication_timeout_test.go +++ b/simplex/replication_timeout_test.go @@ -324,13 +324,13 @@ func TestReplicationRequestIncompleteResponses(t *testing.T) { type collectNotarizationComm struct { lock *sync.Mutex - notarizations map[uint64]*common.Notarization + notarizations map[uint64]*common.RawNotarization testutil.TestNetworkCommunication replicationResponses chan struct{} } -func newCollectNotarizationComm(nodeID common.NodeID, net *testutil.ControlledInMemoryNetwork, notarizations map[uint64]*common.Notarization, lock *sync.Mutex) *collectNotarizationComm { +func newCollectNotarizationComm(nodeID common.NodeID, net *testutil.ControlledInMemoryNetwork, notarizations map[uint64]*common.RawNotarization, lock *sync.Mutex) *collectNotarizationComm { return &collectNotarizationComm{ notarizations: notarizations, TestNetworkCommunication: testutil.NewTestComm(nodeID, net.BasicInMemoryNetwork, testutil.AllowAllMessages), @@ -367,8 +367,12 @@ func (c *collectNotarizationComm) removeFinalizationsFromReplicationResponses(ms for i := 0; i < len(msg.VerifiedReplicationResponse.Data); i++ { qr := msg.VerifiedReplicationResponse.Data[i] if qr.Finalization != nil && c.notarizations[qr.GetRound()] != nil { + rawNotarization := c.notarizations[qr.GetRound()] qr.Finalization = nil - qr.Notarization = c.notarizations[qr.GetRound()] + qr.Notarization = &common.Notarization{ + Vote: rawNotarization.Vote, + QC: rawNotarization.QC.(common.QuorumCertificate), + } } newData = append(newData, qr) } @@ -398,7 +402,7 @@ func TestReplicationRequestWithoutFinalization(t *testing.T) { endDisconnect := uint64(10) net := testutil.NewControlledNetwork(t, nodes) - notarizations := make(map[uint64]*common.Notarization) + notarizations := make(map[uint64]*common.RawNotarization) mapLock := &sync.Mutex{} testConfig := func(nodeID common.NodeID) *testutil.TestNodeConfig { return &testutil.TestNodeConfig{ @@ -627,7 +631,7 @@ func TestReplicationResendsFinalizedBlocksThatFailedVerification(t *testing.T) { // send the finalization to start the replication process e.HandleMessage(&common.Message{ - Finalization: &finalization, + Finalization: finalization.Raw(), }, nodes[0]) // wait for the replication request to be sent for { @@ -637,10 +641,10 @@ func TestReplicationResendsFinalizedBlocksThatFailedVerification(t *testing.T) { } } replicationResponse := &common.ReplicationResponse{ - Data: []common.QuorumRound{ + Data: []common.RawQuorumRound{ { Block: block, - Finalization: &finalization, + Finalization: finalization.Raw(), }, }, } @@ -661,10 +665,10 @@ func TestReplicationResendsFinalizedBlocksThatFailedVerification(t *testing.T) { finalization, _ = testutil.NewFinalizationRecord(t, sigAggr, block, nodes[0:quorum]) replicationResponse = &common.ReplicationResponse{ - Data: []common.QuorumRound{ + Data: []common.RawQuorumRound{ { Block: block, - Finalization: &finalization, + Finalization: finalization.Raw(), }, }, } diff --git a/testutil/comm.go b/testutil/comm.go index 94e3bce1..e7327cb1 100644 --- a/testutil/comm.go +++ b/testutil/comm.go @@ -94,23 +94,23 @@ func (c *TestComm) SetFilter(filter MessageFilter) { func (c *TestComm) maybeTranslateOutgoingToIncomingMessageTypes(msg *common.Message) { if msg.VerifiedReplicationResponse != nil { - data := make([]common.QuorumRound, 0, len(msg.VerifiedReplicationResponse.Data)) + data := make([]common.RawQuorumRound, 0, len(msg.VerifiedReplicationResponse.Data)) for _, verifiedQuorumRound := range msg.VerifiedReplicationResponse.Data { // Outgoing block is of type verified block but incoming block is of type Block, // so we do a type cast because the test block implements both. - quorumRound := common.QuorumRound{} + quorumRound := common.RawQuorumRound{} if verifiedQuorumRound.EmptyNotarization != nil { - quorumRound.EmptyNotarization = verifiedQuorumRound.EmptyNotarization + quorumRound.EmptyNotarization = verifiedQuorumRound.EmptyNotarization.Raw() } if verifiedQuorumRound.VerifiedBlock != nil { quorumRound.Block = verifiedQuorumRound.VerifiedBlock.(common.Block) } if verifiedQuorumRound.Notarization != nil { - quorumRound.Notarization = verifiedQuorumRound.Notarization + quorumRound.Notarization = verifiedQuorumRound.Notarization.Raw() } if verifiedQuorumRound.Finalization != nil { - quorumRound.Finalization = verifiedQuorumRound.Finalization + quorumRound.Finalization = verifiedQuorumRound.Finalization.Raw() } data = append(data, quorumRound) @@ -127,8 +127,8 @@ func (c *TestComm) maybeTranslateOutgoingToIncomingMessageTypes(msg *common.Mess msg.ReplicationResponse = &common.ReplicationResponse{ Data: data, - LatestRound: latestRound, - LatestSeq: latestSeq, + LatestRound: latestRound.Raw(), + LatestSeq: latestSeq.Raw(), } } diff --git a/testutil/long_running/network.go b/testutil/long_running/network.go index f055c5a1..17ac1440 100644 --- a/testutil/long_running/network.go +++ b/testutil/long_running/network.go @@ -173,7 +173,8 @@ func (n *LongRunningInMemoryNetwork) StopAndAssert(tailingMessages bool) { // check all the nodes have the same wal, storage, etc for i, instance := range n.instances { - instance.wal.AssertHealthy(instance.E.BlockDeserializer, instance.E.QCDeserializer) + qcDeserializer := instance.E.QCDeserializerCreator(nil) + instance.wal.AssertHealthy(instance.E.BlockDeserializer, qcDeserializer) if i != 0 { require.NoError(n.t, instance.Storage.Compare(n.instances[0].Storage), "node %d storage does not match node 0 storage", i) } diff --git a/testutil/random_network/node.go b/testutil/random_network/node.go index a12ebb1a..7bafe0da 100644 --- a/testutil/random_network/node.go +++ b/testutil/random_network/node.go @@ -117,7 +117,7 @@ func (n *Node) copyMessage(msg *common.Message) common.Message { rrCopy := *msgCopy.ReplicationResponse // Also copy the Data slice to avoid mutating shared slice - rrCopy.Data = make([]common.QuorumRound, len(msgCopy.ReplicationResponse.Data)) + rrCopy.Data = make([]common.RawQuorumRound, len(msgCopy.ReplicationResponse.Data)) copy(rrCopy.Data, msgCopy.ReplicationResponse.Data) msgCopy.ReplicationResponse = &rrCopy diff --git a/testutil/util.go b/testutil/util.go index fe3efd07..b3e1e902 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -37,7 +37,9 @@ func DefaultTestNodeEpochConfig(t *testing.T, nodeID common.NodeID, comm common. return &TestSignatureAggregator{N: len(weights)} }, BlockDeserializer: &BlockDeserializer{}, - QCDeserializer: &testQCDeserializer{t: t}, + QCDeserializerCreator: func(common.Nodes) common.QCDeserializer { + return &testQCDeserializer{t: t} + }, StartTime: time.Now(), } return conf, wal, storage @@ -92,7 +94,7 @@ func NewTestFinalizeVote(t *testing.T, block common.VerifiedBlock, id common.Nod func InjectTestFinalization(t *testing.T, e *simplex.Epoch, finalization *common.Finalization, from common.NodeID) { err := e.HandleMessage(&common.Message{ - Finalization: finalization, + Finalization: finalization.Raw(), }, from) require.NoError(t, err) } @@ -106,7 +108,7 @@ func InjectTestFinalizeVote(t *testing.T, e *simplex.Epoch, block common.Verifie func InjectTestNotarization(t *testing.T, e *simplex.Epoch, notarization common.Notarization, id common.NodeID) { err := e.HandleMessage(&common.Message{ - Notarization: ¬arization, + Notarization: notarization.Raw(), }, id) require.NoError(t, err) } @@ -115,6 +117,10 @@ type testQCDeserializer struct { t *testing.T } +func (t *testQCDeserializer) ParseQuorumCertificate(rawQC common.RawQuorumCertificate) (common.QuorumCertificate, error) { + return rawQC.(common.QuorumCertificate), nil +} + func (t *testQCDeserializer) DeserializeQuorumCertificate(bytes []byte) (common.QuorumCertificate, error) { var qc []common.Signature rest, err := asn1.Unmarshal(bytes, &qc)