Skip to content

Commit 4cb81c0

Browse files
committed
Drop messages that are no longer useful for GPBFT progression
As a GPBFT instance progresses some messages become irrelevant, in that they do not effectively aid the progress of the instance for participants. Instead, GPBFT offers other built-in mechanisms to aid progress of lagging participants such as selective rebroadcast, propagation of DECIDE messages from the previous instance and finality certificate exchange. The changes here introduce a dedicated error type returned as part of message validation to signal that although a message is valid it is no longer relevant. This error type is then checked by pubsub to avoid further propagation of those messages. This reduces the redundant pubsub traffic for the network participants. Fixes #583
1 parent 5724153 commit 4cb81c0

File tree

5 files changed

+61
-1
lines changed

5 files changed

+61
-1
lines changed

gpbft/errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ var (
2525
//
2626
// See SupplementalData.
2727
ErrValidationWrongSupplement = newValidationError("unexpected supplemental data")
28+
// ErrValidationNotRelevant signals that a message is valid but not relevant at the current instance,
29+
// and is not worth propagating to others.
30+
ErrValidationNotRelevant = newValidationError("message is valid but not relevant")
2831

2932
// ErrReceivedWrongInstance signals that a message is received with mismatching instance ID.
3033
ErrReceivedWrongInstance = errors.New("received message for wrong instance")

gpbft/errors_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func TestValidationError_SentinelValues(t *testing.T) {
1717
{name: "ErrValidationInvalid", subject: ErrValidationInvalid},
1818
{name: "ErrValidationWrongBase", subject: ErrValidationWrongBase},
1919
{name: "ErrValidationWrongSupplement", subject: ErrValidationWrongSupplement},
20+
{name: "ErrValidationNotRelevant", subject: ErrValidationNotRelevant},
2021
}
2122
for _, test := range tests {
2223
t.Run(test.name, func(t *testing.T) {

gpbft/participant.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ func (p *Participant) ValidateMessage(msg *GMessage) (valid ValidatedMessage, er
174174
} else if alreadyValidated, err := p.validationCache.Contains(msg.Vote.Instance, messageCacheNamespace, buf.Bytes()); err != nil {
175175
log.Errorw("failed to check already validated messages", "err", err)
176176
} else if alreadyValidated {
177+
if !p.isMessageRelevant(msg) {
178+
return nil, ErrValidationNotRelevant
179+
}
177180
metrics.validationCache.Add(context.TODO(), 1, metric.WithAttributes(attrCacheHit, attrCacheKindMessage))
178181
return &validatedMessage{msg: msg}, nil
179182
} else {
@@ -243,6 +246,11 @@ func (p *Participant) ValidateMessage(msg *GMessage) (valid ValidatedMessage, er
243246
return nil, fmt.Errorf("message %v has unexpected justification: %w", msg, ErrValidationInvalid)
244247
}
245248

249+
if !p.isMessageRelevant(msg) {
250+
// Message is valid but no longer relevant in the context of progressing GPBFT.
251+
return nil, ErrValidationNotRelevant
252+
}
253+
246254
if cacheMessage {
247255
if _, err := p.validationCache.Add(msg.Vote.Instance, messageCacheNamespace, buf.Bytes()); err != nil {
248256
log.Warnw("failed to cache to already validated message", "err", err)
@@ -366,6 +374,46 @@ func (p *Participant) validateJustification(msg *GMessage, comt *committee) erro
366374
return nil
367375
}
368376

377+
// isMessageRelevant determines whether a given message is useful in terms of
378+
// aiding the progress of an instance to the best of our knowledge.
379+
func (p *Participant) isMessageRelevant(msg *GMessage) bool {
380+
p.instanceMutex.Lock()
381+
defer p.instanceMutex.Unlock()
382+
var currentRound uint64
383+
var currentPhase Phase
384+
if p.gpbft != nil {
385+
currentRound = p.gpbft.round
386+
currentPhase = p.gpbft.phase
387+
}
388+
389+
// Relevant messages are:
390+
// 1. DECIDE messages from previous instance,
391+
// 2. some messages from current instance (pending further checks), or
392+
// 3. any message from future instance.
393+
switch msg.Vote.Instance {
394+
case p.currentInstance:
395+
// Message is from the current round; proceed to check other conditions.
396+
case min(0, p.currentInstance-1):
397+
return msg.Vote.Phase == DECIDE_PHASE
398+
default:
399+
return msg.Vote.Instance > p.currentInstance
400+
}
401+
402+
// Whe we are at DECIDE phase the only relevant messages are DECIDE messages.
403+
// Otherwise, relevant messages are either QUALITY, DECIDE, messages from
404+
// previous round, current round or future rounds.
405+
switch {
406+
case currentPhase == DECIDE_PHASE:
407+
return msg.Vote.Phase == DECIDE_PHASE
408+
case msg.Vote.Phase == QUALITY_PHASE,
409+
msg.Vote.Phase == DECIDE_PHASE,
410+
msg.Vote.Round >= min(0, currentRound-1):
411+
return true
412+
default:
413+
return false
414+
}
415+
}
416+
369417
// Receives a validated Granite message from some other participant.
370418
func (p *Participant) ReceiveMessage(vmsg ValidatedMessage) (err error) {
371419
if !p.apiMutex.TryLock() {

host.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ func (h *gpbftRunner) validatePubsubMessage(ctx context.Context, _ peer.ID, msg
324324
case errors.Is(err, gpbft.ErrValidationTooOld):
325325
// we got the message too late
326326
return pubsub.ValidationIgnore
327+
case errors.Is(err, gpbft.ErrValidationNotRelevant):
328+
// The message is valid but will not effectively aid progress of GPBFT. Ignore it
329+
// to stop its further propagation across the network.
330+
return pubsub.ValidationIgnore
327331
case errors.Is(err, gpbft.ErrValidationNoCommittee):
328332
log.Debugf("commitee error during validation: %+v", err)
329333
return pubsub.ValidationIgnore

sim/sim.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package sim
22

33
import (
4+
"errors"
45
"fmt"
56
"strings"
67

@@ -131,7 +132,10 @@ func (s *Simulation) Run(instanceCount uint64, maxRounds uint64) error {
131132
}
132133
var err error
133134
moreTicks, err = s.network.Tick(s.adversary)
134-
if err != nil {
135+
switch {
136+
case errors.Is(err, gpbft.ErrValidationNotRelevant):
137+
// Ignore error signalling valid messages that are no longer useful for the progress of GPBFT.
138+
case err != nil:
135139
return fmt.Errorf("error performing simulation phase: %w", err)
136140
}
137141
}

0 commit comments

Comments
 (0)