-
Notifications
You must be signed in to change notification settings - Fork 179
/
cluster_committee.go
134 lines (114 loc) · 4.41 KB
/
cluster_committee.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package committees
import (
"fmt"
"github.com/onflow/flow-go/consensus/hotstuff"
"github.com/onflow/flow-go/consensus/hotstuff/committees/leader"
"github.com/onflow/flow-go/consensus/hotstuff/model"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/model/flow/filter"
"github.com/onflow/flow-go/state/protocol"
"github.com/onflow/flow-go/storage"
)
// Cluster represents the committee for a cluster of collection nodes. Cluster
// committees are epoch-scoped.
//
// Clusters build blocks on a cluster chain but must obtain identity table
// information from the main chain. Thus, block ID parameters in this Committee
// implementation reference blocks on the cluster chain, which in turn reference
// blocks on the main chain - this implementation manages that translation.
type Cluster struct {
state protocol.State
payloads storage.ClusterPayloads
me flow.Identifier
// pre-computed leader selection for the full lifecycle of the cluster
selection *leader.LeaderSelection
// a filter that returns all members of the cluster committee allowed to vote
clusterMemberFilter flow.IdentityFilter
// initial set of cluster members, WITHOUT updated weight
initialClusterMembers flow.IdentityList
}
var _ hotstuff.Committee = (*Cluster)(nil)
func NewClusterCommittee(
state protocol.State,
payloads storage.ClusterPayloads,
cluster protocol.Cluster,
epoch protocol.Epoch,
me flow.Identifier,
) (*Cluster, error) {
selection, err := leader.SelectionForCluster(cluster, epoch)
if err != nil {
return nil, fmt.Errorf("could not compute leader selection for cluster: %w", err)
}
com := &Cluster{
state: state,
payloads: payloads,
me: me,
selection: selection,
clusterMemberFilter: filter.And(
cluster.Members().Selector(),
filter.Not(filter.Ejected),
filter.HasWeight(true),
),
initialClusterMembers: cluster.Members(),
}
return com, nil
}
// Identities returns the identities of all cluster members that are authorized to
// participate at the given block. The order of the identities is the canonical order.
func (c *Cluster) Identities(blockID flow.Identifier) (flow.IdentityList, error) {
// blockID is a collection block not a block produced by consensus,
// to query the identities from protocol state, we need to use the reference block id from the payload
//
// first retrieve the cluster block payload
payload, err := c.payloads.ByBlockID(blockID)
if err != nil {
return nil, fmt.Errorf("could not get cluster payload: %w", err)
}
// an empty reference block ID indicates a root block
isRootBlock := payload.ReferenceBlockID == flow.ZeroID
// use the initial cluster members for root block
if isRootBlock {
return c.initialClusterMembers, nil
}
// otherwise use the snapshot given by the reference block
identities, err := c.state.AtBlockID(payload.ReferenceBlockID).Identities(c.clusterMemberFilter) // remove ejected nodes
return identities, err
}
func (c *Cluster) Identity(blockID flow.Identifier, nodeID flow.Identifier) (*flow.Identity, error) {
// first retrieve the cluster block payload
payload, err := c.payloads.ByBlockID(blockID)
if err != nil {
return nil, fmt.Errorf("could not get cluster payload: %w", err)
}
// an empty reference block ID indicates a root block
isRootBlock := payload.ReferenceBlockID == flow.ZeroID
// use the initial cluster members for root block
if isRootBlock {
identity, ok := c.initialClusterMembers.ByNodeID(nodeID)
if !ok {
return nil, model.NewInvalidSignerErrorf("node %v is not an authorized hotstuff participant", nodeID)
}
return identity, nil
}
// otherwise use the snapshot given by the reference block
identity, err := c.state.AtBlockID(payload.ReferenceBlockID).Identity(nodeID)
if protocol.IsIdentityNotFound(err) {
return nil, model.NewInvalidSignerErrorf("%v is not a valid node id at block %v: %w", nodeID, payload.ReferenceBlockID, err)
}
if err != nil {
return nil, fmt.Errorf("could not get identity for node (id=%x): %w", nodeID, err)
}
if !c.clusterMemberFilter(identity) {
return nil, model.NewInvalidSignerErrorf("node %v is not an authorized hotstuff cluster member", nodeID)
}
return identity, nil
}
func (c *Cluster) LeaderForView(view uint64) (flow.Identifier, error) {
return c.selection.LeaderForView(view)
}
func (c *Cluster) Self() flow.Identifier {
return c.me
}
func (c *Cluster) DKG(_ flow.Identifier) (hotstuff.DKG, error) {
panic("queried DKG of cluster committee")
}