Skip to content

Commit 27188ec

Browse files
bors[bot]durkmurderjordanschalm
authored
Merge #4086
4086: Port #4063 to master r=jordanschalm a=jordanschalm - Ports #4063 to `master` - Adds additional documentation of the issue, and a link to an issue 7364a43 Co-authored-by: Yurii Oleksyshyn <[email protected]> Co-authored-by: Jordan Schalm <[email protected]>
2 parents 0eabbd7 + 9e73621 commit 27188ec

File tree

6 files changed

+33
-13
lines changed

6 files changed

+33
-13
lines changed

consensus/hotstuff/committee.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,16 @@ type Replicas interface {
6464
DKG(view uint64) (DKG, error)
6565

6666
// IdentitiesByEpoch returns a list of the legitimate HotStuff participants for the epoch
67-
// given by the input view. The list of participants is filtered by the provided selector.
67+
// given by the input view.
6868
// The returned list of HotStuff participants:
6969
// * contains nodes that are allowed to submit votes or timeouts within the given epoch
7070
// (un-ejected, non-zero weight at the beginning of the epoch)
7171
// * is ordered in the canonical order
7272
// * contains no duplicates.
73-
// The list of all legitimate HotStuff participants for the given epoch can be obtained by using `filter.Any`
7473
//
7574
// CAUTION: DO NOT use this method for validating block proposals.
75+
// CAUTION: This method considers epochs outside of Previous, Current, Next, w.r.t. the
76+
// finalized block, to be unknown. https://github.com/onflow/flow-go/issues/4085
7677
//
7778
// Returns the following expected errors for invalid inputs:
7879
// * model.ErrViewForUnknownEpoch if no epoch containing the given view is known
@@ -82,6 +83,9 @@ type Replicas interface {
8283

8384
// IdentityByEpoch returns the full Identity for specified HotStuff participant.
8485
// The node must be a legitimate HotStuff participant with NON-ZERO WEIGHT at the specified block.
86+
// CAUTION: This method considers epochs outside of Previous, Current, Next, w.r.t. the
87+
// finalized block, to be unknown. https://github.com/onflow/flow-go/issues/4085
88+
//
8589
// ERROR conditions:
8690
// * model.InvalidSignerError if participantID does NOT correspond to an authorized HotStuff participant at the specified block.
8791
//
@@ -104,15 +108,13 @@ type DynamicCommittee interface {
104108
Replicas
105109

106110
// IdentitiesByBlock returns a list of the legitimate HotStuff participants for the given block.
107-
// The list of participants is filtered by the provided selector.
108111
// The returned list of HotStuff participants:
109112
// * contains nodes that are allowed to submit proposals, votes, and timeouts
110113
// (un-ejected, non-zero weight at current block)
111114
// * is ordered in the canonical order
112115
// * contains no duplicates.
113-
// The list of all legitimate HotStuff participants for the given epoch can be obtained by using `filter.Any`
114116
//
115-
// TODO - do we need this, if we are only checking a single proposer ID?
117+
// No errors are expected during normal operation.
116118
IdentitiesByBlock(blockID flow.Identifier) (flow.IdentityList, error)
117119

118120
// IdentityByBlock returns the full Identity for specified HotStuff participant.
@@ -130,7 +132,6 @@ type BlockSignerDecoder interface {
130132
// consensus committee has reached agreement on validity of parent block. Consequently, the
131133
// returned IdentifierList contains the consensus participants that signed the parent block.
132134
// Expected Error returns during normal operations:
133-
// - model.ErrViewForUnknownEpoch if the given block's parent is within an unknown epoch
134135
// - signature.InvalidSignerIndicesError if signer indices included in the header do
135136
// not encode a valid subset of the consensus committee
136137
DecodeSignerIDs(header *flow.Header) (flow.IdentifierList, error)

consensus/hotstuff/committees/consensus_committee.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,17 @@ func NewConsensusCommittee(state protocol.State, me flow.Identifier) (*Consensus
187187
return com, nil
188188
}
189189

190-
// Identities returns the identities of all authorized consensus participants at the given block.
190+
// IdentitiesByBlock returns the identities of all authorized consensus participants at the given block.
191191
// The order of the identities is the canonical order.
192+
// No errors are expected during normal operation.
192193
func (c *Consensus) IdentitiesByBlock(blockID flow.Identifier) (flow.IdentityList, error) {
193194
il, err := c.state.AtBlockID(blockID).Identities(filter.IsVotingConsensusCommitteeMember)
194195
return il, err
195196
}
196197

198+
// IdentityByBlock returns the identity of the node with the given node ID at the given block.
199+
// ERROR conditions:
200+
// - model.InvalidSignerError if participantID does NOT correspond to an authorized HotStuff participant at the specified block.
197201
func (c *Consensus) IdentityByBlock(blockID flow.Identifier, nodeID flow.Identifier) (*flow.Identity, error) {
198202
identity, err := c.state.AtBlockID(blockID).Identity(nodeID)
199203
if err != nil {
@@ -210,6 +214,8 @@ func (c *Consensus) IdentityByBlock(blockID flow.Identifier, nodeID flow.Identif
210214

211215
// IdentitiesByEpoch returns the committee identities in the epoch which contains
212216
// the given view.
217+
// CAUTION: This method considers epochs outside of Previous, Current, Next, w.r.t. the
218+
// finalized block, to be unknown. https://github.com/onflow/flow-go/issues/4085
213219
//
214220
// Error returns:
215221
// - model.ErrViewForUnknownEpoch if no committed epoch containing the given view is known.
@@ -225,6 +231,8 @@ func (c *Consensus) IdentitiesByEpoch(view uint64) (flow.IdentityList, error) {
225231

226232
// IdentityByEpoch returns the identity for the given node ID, in the epoch which
227233
// contains the given view.
234+
// CAUTION: This method considers epochs outside of Previous, Current, Next, w.r.t. the
235+
// finalized block, to be unknown. https://github.com/onflow/flow-go/issues/4085
228236
//
229237
// Error returns:
230238
// - model.ErrViewForUnknownEpoch if no committed epoch containing the given view is known.

consensus/hotstuff/signature/block_signer_decoder.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
// BlockSignerDecoder is a wrapper around the `hotstuff.DynamicCommittee`, which implements
14-
// the auxilluary logic for de-coding signer indices of a block (header) to full node IDs
14+
// the auxiliary logic for de-coding signer indices of a block (header) to full node IDs
1515
type BlockSignerDecoder struct {
1616
hotstuff.DynamicCommittee
1717
}
@@ -27,7 +27,6 @@ var _ hotstuff.BlockSignerDecoder = (*BlockSignerDecoder)(nil)
2727
// consensus committee has reached agreement on validity of parent block. Consequently, the
2828
// returned IdentifierList contains the consensus participants that signed the parent block.
2929
// Expected Error returns during normal operations:
30-
// - model.ErrViewForUnknownEpoch if the given block's parent is within an unknown epoch
3130
// - signature.InvalidSignerIndicesError if signer indices included in the header do
3231
// not encode a valid subset of the consensus committee
3332
func (b *BlockSignerDecoder) DecodeSignerIDs(header *flow.Header) (flow.IdentifierList, error) {
@@ -36,12 +35,20 @@ func (b *BlockSignerDecoder) DecodeSignerIDs(header *flow.Header) (flow.Identifi
3635
return []flow.Identifier{}, nil
3736
}
3837

38+
// we will use IdentitiesByEpoch since it's a faster call and avoids DB lookup
3939
members, err := b.IdentitiesByEpoch(header.ParentView)
4040
if err != nil {
4141
if errors.Is(err, model.ErrViewForUnknownEpoch) {
42-
return nil, fmt.Errorf("could not retrieve consensus participants for view %d: %w", header.ParentView, err)
42+
// possibly, we request epoch which is far behind in the past, in this case we won't have it in cache.
43+
// try asking by parent ID
44+
members, err = b.IdentitiesByBlock(header.ParentID)
45+
if err != nil {
46+
return nil, fmt.Errorf("could not retrieve identities for block %x with QC view %d for parent %x: %w",
47+
header.ID(), header.ParentView, header.ParentID, err)
48+
}
49+
} else {
50+
return nil, fmt.Errorf("unexpected error retrieving identities for block %v: %w", header.ID(), err)
4351
}
44-
return nil, fmt.Errorf("unexpected error retrieving identities for block %v: %w", header.ID(), err)
4552
}
4653
signerIDs, err := signature.DecodeSignerIndicesToIdentifiers(members.NodeIDs(), header.ParentVoterIndices)
4754
if err != nil {

consensus/hotstuff/signature/block_signer_decoder_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package signature
22

33
import (
44
"errors"
5+
"fmt"
56
"testing"
67

78
"github.com/stretchr/testify/mock"
@@ -83,11 +84,12 @@ func (s *blockSignerDecoderSuite) Test_UnexpectedCommitteeException() {
8384
// It should propagate the sentinel error model.ErrViewForUnknownEpoch from Committee.
8485
func (s *blockSignerDecoderSuite) Test_UnknownEpoch() {
8586
*s.committee = *hotstuff.NewDynamicCommittee(s.T())
86-
s.committee.On("IdentitiesByEpoch", mock.Anything).Return(nil, model.ErrViewForUnknownEpoch)
87+
s.committee.On("IdentitiesByEpoch", s.block.Header.ParentView).Return(nil, model.ErrViewForUnknownEpoch)
88+
s.committee.On("IdentitiesByBlock", s.block.Header.ParentID).Return(nil, fmt.Errorf(""))
8789

8890
ids, err := s.decoder.DecodeSignerIDs(s.block.Header)
8991
require.Empty(s.T(), ids)
90-
require.ErrorIs(s.T(), err, model.ErrViewForUnknownEpoch)
92+
require.Error(s.T(), err)
9193
}
9294

9395
// Test_InvalidIndices verifies that `BlockSignerDecoder` returns

module/epochs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type QCContractClient interface {
4646
}
4747

4848
// EpochLookup enables looking up epochs by view.
49+
// CAUTION: EpochLookup should only be used for querying the previous, current, or next epoch.
4950
type EpochLookup interface {
5051

5152
// EpochForViewWithFallback returns the counter of the epoch that the input view belongs to.

module/epochs/epoch_lookup.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ func (cache *epochRangeCache) add(epoch epochRange) error {
100100
}
101101

102102
// EpochLookup implements the EpochLookup interface using protocol state to match views to epochs.
103+
// CAUTION: EpochLookup should only be used for querying the previous, current, or next epoch.
103104
type EpochLookup struct {
104105
state protocol.State
105106
mu sync.RWMutex

0 commit comments

Comments
 (0)