Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dynamic Protocol State] Split flow.Identity in flow.IdentitySkeleton and flow.DynamicIdentity #4545

Merged

Conversation

durkmurder
Copy link
Member

https://github.com/dapperlabs/flow-go/issues/6232

Context

This PR updates flow.Identity to split it in flow.IdentitySkeleton and flow.DynamicIdentity.
As part of this PR:

  • Refactored flow.Identity
  • Now flow.Identity has InitialWeight(part of IdentitySkeleton) and Weight(part of flow.DynamicIdentity)
  • Updated usages of flow.Identity
  • Updated consensus committee to return flow.IdentitySkeleton for epoch-level calls(Replicas interface) and flow.Identity for block-level calls(DynamicCommittee interface)
  • Updated all usages in consensus code to work with IdentitySkeleton where applicable.

⚠️ Due to introducing InitialWeight we need to be careful when we filter identities by weight. InitialWeight will be always positive even if identity is not part of the current epoch(for instance it's participant of next epoch). The only reliable weight for filtering is flow.DynamicIdentity.Weight which represents current weight.

As part of this PR I didn't update epoch structures to return InitialIdentites or EpochSetup.Participants to be IdentitySkeletons since I am not sure we can and want to omit Weight for epoch setup. This needs an extra round of discussions.

@codecov-commenter
Copy link

codecov-commenter commented Jul 7, 2023

Codecov Report

Merging #4545 (a97f4c1) into feature/dynamic-protocol-state (fffb613) will decrease coverage by 0.06%.
The diff coverage is 32.93%.

@@                        Coverage Diff                         @@
##           feature/dynamic-protocol-state    #4545      +/-   ##
==================================================================
- Coverage                           54.44%   54.39%   -0.06%     
==================================================================
  Files                                 912      912              
  Lines                               85165    85268     +103     
==================================================================
+ Hits                                46372    46385      +13     
- Misses                              35215    35309      +94     
+ Partials                             3578     3574       -4     
Flag Coverage Δ
unittests 54.39% <32.93%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Changed Coverage Δ
.../bootstrap/transit/cmd/generate_root_block_vote.go 3.12% <0.00%> (ø)
cmd/scaffold.go 14.83% <0.00%> (ø)
consensus/hotstuff/committees/leader/cluster.go 0.00% <0.00%> (ø)
consensus/hotstuff/committees/leader/consensus.go 0.00% <0.00%> (ø)
consensus/hotstuff/committees/metrics_wrapper.go 0.00% <0.00%> (ø)
consensus/hotstuff/committees/static.go 0.00% <0.00%> (ø)
consensus/hotstuff/validator/metrics_wrapper.go 0.00% <0.00%> (ø)
engine/access/apiproxy/access_api_proxy.go 1.25% <0.00%> (ø)
model/flow/epoch.go 48.45% <ø> (ø)
module/local/me_nokey.go 0.00% <0.00%> (ø)
... and 22 more

... and 11 files with indirect coverage changes

consensus/hotstuff/committees/cluster_committee.go Outdated Show resolved Hide resolved
@@ -401,11 +403,11 @@ func TestZeroWeightNodeWillNotBeSelected(t *testing.T) {
t.Run("if there is only 1 node has weight, then it will be always be the leader and the only leader", func(t *testing.T) {
toolRng := getPRG(t, someSeed)

identities := unittest.IdentityListFixture(1000, unittest.WithWeight(0))
identities := unittest.IdentityListFixture(1000, unittest.WithInitialWeight(0)).ToSkeleton()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting a point to clarify here, eventually. This shouldn't be addressed in this PR.

  • Leader selection is determined based on initial weight. Let some node $N$'s initial weight when it first registers be $W_0$
  • Suppose we process a weight-changing event $\delta$ which results in node $N$'s dynamic weight becoming $W_1$, and $W_1 \ll W_0$
  • In the next epoch, $N$'s leader assignment intuitively should be based on its new weight, at the beginning of that epoch.

Question: Does initial weight persist across epochs? Does the initial weight field mean (1) "initial weight when the node first registered" or (2) "initial weight as of the beginning of this epoch".

I think (2) is most intuitive and useful, but we should align and clarify documentation here, when we get to it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on option (2)

engine/access/rpc/engine.go Outdated Show resolved Hide resolved
model/convert/service_event.go Show resolved Hide resolved
model/flow/identity.go Outdated Show resolved Hide resolved
model/flow/identity.go Outdated Show resolved Hide resolved
model/flow/identity.go Show resolved Hide resolved
model/flow/identity.go Show resolved Hide resolved
model/flow/identity.go Outdated Show resolved Hide resolved
@@ -37,9 +37,18 @@ type Identity struct {
// Role is the node's role in the network and defines its abilities and
// responsibilities.
Role Role
// InitialWeight is a 'trust score' initially assigned by EpochSetup event after
// the staking phase. The initial weights define the supermajority thresholds for
// the cluster and security node consensus throughout the Epoch.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// the cluster and security node consensus throughout the Epoch.
// the cluster and security nodes' consensus throughout the Epoch.

model/flow/identity.go Show resolved Hide resolved
• minor optimization avoiding repetitive memory allocation during slice append
• minor code simplifications
// Identity is combined from static and dynamic part and represents the full public identity of one network participant (node).
type Identity struct {
IdentitySkeleton
DynamicIdentity
}

// ParseIdentity parses a string representation of an identity.
Copy link
Member

@AlexHentschel AlexHentschel Aug 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if method ParseIdentity is used outside of flow-go, but my IDE says it is not used within the package. Maybe it is legacy code (?) We could remove it, or depreciate it?

model/convert/service_event.go Show resolved Hide resolved
Copy link
Member

@AlexHentschel AlexHentschel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Committed a few cosmetic changes (largely just documentation and minor code cleanups) directly to the branch.

consensus/hotstuff/committees/consensus_committee.go Outdated Show resolved Hide resolved
@@ -69,8 +69,8 @@ type EpochSetup struct {
DKGPhase2FinalView uint64 // the final view of DKG phase 2
DKGPhase3FinalView uint64 // the final view of DKG phase 3
FinalView uint64 // the final view of the epoch
Participants IdentityList // all participants of the epoch
Assignments AssignmentList // cluster assignment for the epoch
Participants IdentityList // all participants of the epoch in canonical order
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I feel this should ideally be of type IdentitySkeleton, same as the return value of
    // InitialIdentities returns the identities for this epoch as they were
    // specified in the EpochSetup service event.
    // Error returns:
    // * protocol.ErrNoPreviousEpoch - if the epoch represents a previous epoch which does not exist.
    // * protocol.ErrNextEpochNotSetup - if the epoch represents a next epoch which has not been set up.
    // * state.ErrUnknownSnapshotReference - if the epoch is queried from an unresolvable snapshot.
    InitialIdentities() (flow.IdentityList, error)
  • Would be nice if we could establish a convention on canonical ordering as well and document it.

Probably, both points are beyond the scope of this PR.

@@ -401,11 +403,11 @@ func TestZeroWeightNodeWillNotBeSelected(t *testing.T) {
t.Run("if there is only 1 node has weight, then it will be always be the leader and the only leader", func(t *testing.T) {
toolRng := getPRG(t, someSeed)

identities := unittest.IdentityListFixture(1000, unittest.WithWeight(0))
identities := unittest.IdentityListFixture(1000, unittest.WithInitialWeight(0)).ToSkeleton()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on option (2)

Comment on lines 363 to 365
slices.SortFunc(decodedSigners, func(lhs, rhs *flow.IdentitySkeleton) bool {
return order.IdentifierCanonical(lhs.NodeID, rhs.NodeID)
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By construction, the decoded signer set should be canonically ordered. I think it would be beneficial to test this here, i.e. remove the output ordering so the test would fail, if DecodeSignerIndicesToIdentities violates the ordering convention

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated code in bb6353b

Copy link
Member

@jordanschalm jordanschalm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@jordanschalm
Copy link
Member

bors merge

bors bot added a commit that referenced this pull request Aug 23, 2023
4545: [Dynamic Protocol State] Split `flow.Identity` in `flow.IdentitySkeleton` and `flow.DynamicIdentity` r=jordanschalm a=durkmurder

https://github.com/dapperlabs/flow-go/issues/6232

### Context

This PR updates `flow.Identity` to split it in `flow.IdentitySkeleton` and `flow.DynamicIdentity`. 
As part of this PR:
- Refactored `flow.Identity`
- Now `flow.Identity` has `InitialWeight`(part of `IdentitySkeleton`) and `Weight`(part of `flow.DynamicIdentity`) 
- Updated usages of `flow.Identity`
- Updated consensus committee to return `flow.IdentitySkeleton` for epoch-level calls(`Replicas` interface) and `flow.Identity` for block-level calls(`DynamicCommittee` interface)
- Updated all usages in consensus code to work with `IdentitySkeleton` where applicable. 

:warning: Due to introducing `InitialWeight` we need to be careful when we filter identities by weight. `InitialWeight` will be always positive even if identity is not part of the current epoch(for instance it's participant of next epoch). The only reliable weight for filtering is `flow.DynamicIdentity.Weight` which represents current weight. 

As part of this PR I didn't update epoch structures to return `InitialIdentites` or `EpochSetup.Participants` to be IdentitySkeletons since I am not sure we can and want to omit `Weight` for epoch setup. This needs an extra round of discussions. 

Co-authored-by: Yurii Oleksyshyn <yuraolex@gmail.com>
Co-authored-by: Alexander Hentschel <alex.hentschel@axiomzen.co>
Co-authored-by: Jordan Schalm <jordan@dapperlabs.com>
@bors
Copy link
Contributor

bors bot commented Aug 23, 2023

Build failed:

@jordanschalm jordanschalm merged commit 8151d19 into feature/dynamic-protocol-state Aug 23, 2023
@jordanschalm jordanschalm deleted the yurii/6232-static-identity-model branch August 23, 2023 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants