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

Integrated multikey in consensus #4463

Merged
merged 5 commits into from Sep 20, 2022

Conversation

iulianpascalau
Copy link
Contributor

Description of the reasoning behind the pull request (what feature was missing / how the problem was manifesting itself / what was the motive behind the refactoring)

  • integrated multikey components in consensus

Proposed Changes

  • integration

Testing procedure

  • N/A yet

# Conflicts:
#	integrationTests/testProcessorNode.go
@codecov-commenter
Copy link

Codecov Report

Base: 73.94% // Head: 73.89% // Decreases project coverage by -0.04% ⚠️

Coverage data is based on head (4a971d5) compared to base (d61698d).
Patch coverage: 65.04% of modified lines in pull request are covered.

Additional details and impacted files
@@                 Coverage Diff                  @@
##           feat/multisigner    #4463      +/-   ##
====================================================
- Coverage             73.94%   73.89%   -0.05%     
====================================================
  Files                   698      698              
  Lines                 88808    89033     +225     
====================================================
+ Hits                  65665    65789     +124     
- Misses                18207    18285      +78     
- Partials               4936     4959      +23     
Impacted Files Coverage Δ
factory/consensusComponents.go 0.00% <0.00%> (ø)
consensus/spos/bls/subroundStartRound.go 69.94% <35.71%> (-2.83%) ⬇️
consensus/spos/bls/subroundSignature.go 64.62% <43.33%> (-13.34%) ⬇️
consensus/spos/bls/subroundBlock.go 75.85% <46.87%> (-2.76%) ⬇️
consensus/spos/bls/subroundEndRound.go 63.89% <58.92%> (+0.28%) ⬆️
consensus/broadcast/metaChainMessenger.go 59.55% <66.66%> (-0.70%) ⬇️
consensus/spos/worker.go 81.74% <71.42%> (-0.25%) ⬇️
consensus/broadcast/shardChainMessenger.go 56.25% <76.47%> (-0.42%) ⬇️
consensus/broadcast/commonMessenger.go 81.98% <87.09%> (+0.16%) ⬆️
consensus/spos/roundConsensus.go 95.09% <90.90%> (-1.50%) ⬇️
... and 9 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

if sr.IsNodeInConsensusGroup(sr.SelfPubKey()) {
selfJobDone = sr.IsSelfJobDone(sr.Current())
}
allInOneJobDone := sr.IsMultiKeyJobDone(sr.Current())
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
allInOneJobDone := sr.IsMultiKeyJobDone(sr.Current())
multiKeyJobDone := sr.IsMultiKeyJobDone(sr.Current())

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@@ -302,3 +310,71 @@ func (sr *subroundSignature) remainingTime() time.Duration {

return remainigTime
}

func (sr *subroundSignature) doSignatureJobForManagedKeys() bool {
isAllInOneLeader := sr.IsMultiKeyLeaderInCurrentRound()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
isAllInOneLeader := sr.IsMultiKeyLeaderInCurrentRound()
isMultiKeyLeader := sr.IsMultiKeyLeaderInCurrentRound()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

if cns.IsNodeInConsensusGroup(cns.SelfPubKey()) {
selfJobDone = cns.IsSelfJobDone(currentSubroundId)
}
allInOneJobDone := cns.IsMultiKeyJobDone(currentSubroundId)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
allInOneJobDone := cns.IsMultiKeyJobDone(currentSubroundId)
multiKeyJobDone := cns.IsMultiKeyJobDone(currentSubroundId)

Copy link
Contributor

Choose a reason for hiding this comment

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

What about this approach?

multiKeyJobDone := true
if cns.IsMultiKeyInConsensusGroup() {
multiKeyJobDone := cns.IsMultiKeyJobDone(currentSubroundId)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

refactored for consistency reasons

}

// IncrementRoundsWithoutReceivedMessages increments the number of rounds without received messages on a provided public key
// TODO(jls) add tests
Copy link
Contributor

Choose a reason for hiding this comment

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

👀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed TODO as tests already exist

@@ -164,7 +164,7 @@ func (sender *multikeyHeartbeatSender) sendMessageForKey(pkBytes []byte) error {
versionNumber,
name,
identity,
uint32(core.RegularPeer), // force all in one peers to be of type regular peers
uint32(core.RegularPeer), // force multi key handled peers to be of type regular peers
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

if err != nil {
log.Error("setup error in commonMessenger.broadcast - public key is managed but does not contain p2p sign info",
"pk", pkBytes, "error", err)
return
Copy link
Contributor

Choose a reason for hiding this comment

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

Thinking if it is better to call cm.messenger.Broadcast(topic, data) before return. Exactly as the approach used in getPrivateKey method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thought about this. I think part of the messages will be discarded because it will broadcast data as an observer on topics on which data needs to be sent only by validators.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok

Copy link
Contributor

Choose a reason for hiding this comment

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

here you need to broadcast on behalf of someone else, but you cannot do it since you don't have the required p2p data, so I think it is fine to simply return. Otherwise the message would anyway fail on verification and be dropped.

if sr.IsNodeInConsensusGroup(sr.SelfPubKey()) {
selfJobDone = sr.IsSelfJobDone(sr.Current())
}
allInOneJobDone := sr.IsMultiKeyJobDone(sr.Current())
Copy link
Contributor

Choose a reason for hiding this comment

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

What about this approach?

multiKeyJobDone := true
if sr.IsMultiKeyInConsensusGroup() {
     multiKeyJobDone := sr.IsMultiKeyJobDone(sr.Current())
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

refactored

if cns.IsNodeInConsensusGroup(cns.SelfPubKey()) {
selfJobDone = cns.IsSelfJobDone(currentSubroundId)
}
allInOneJobDone := cns.IsMultiKeyJobDone(currentSubroundId)
Copy link
Contributor

Choose a reason for hiding this comment

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

What about this approach?

multiKeyJobDone := true
if cns.IsMultiKeyInConsensusGroup() {
multiKeyJobDone := cns.IsMultiKeyJobDone(currentSubroundId)
}

@AdoAdoAdo AdoAdoAdo self-requested a review September 15, 2022 07:35

return nil
}

func (cm *commonMessenger) getPrivateKey(message *consensus.Message) crypto.PrivateKey {
publicKey := message.PubKey
if !cm.keysHolder.IsKeyManagedByCurrentNode(publicKey) {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we extract the key management logic out of the consensus?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, will try

Copy link
Contributor Author

Choose a reason for hiding this comment

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

extracted

if err != nil {
log.Error("setup error in commonMessenger.broadcast - public key is managed but does not contain p2p sign info",
"pk", pkBytes, "error", err)
return
Copy link
Contributor

Choose a reason for hiding this comment

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

here you need to broadcast on behalf of someone else, but you cannot do it since you don't have the required p2p data, so I think it is fine to simply return. Otherwise the message would anyway fail on verification and be dropped.


// SyncDelay is used so that nodes have enough time to sync
var SyncDelay = time.Second / 5
var SyncDelay = time.Second * 2
Copy link
Contributor

Choose a reason for hiding this comment

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

this is a large increase
I think we need to investigate where the increase comes from.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

reverted

@@ -204,6 +206,30 @@ func (sr *Subround) ConsensusChannel() chan bool {
return sr.consensusStateChangedChannel
}

// GetAssociatedPid returns the associated PeerID to the provided public key bytes
func (sr *Subround) GetAssociatedPid(pkBytes []byte) core.PeerID {
Copy link
Contributor

Choose a reason for hiding this comment

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

This logic I think needs to be moved to a separate component, so consensus does not need to manage itself multiple peers, but delegate this to that component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, will try

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// IsMultiKeyLeaderInCurrentRound method checks if one of the nodes which are controlled by this instance
// is leader in the current round
func (cns *ConsensusState) IsMultiKeyLeaderInCurrentRound() bool {
managedKeys := cns.keysHolder.GetManagedKeysByCurrentNode()
Copy link
Contributor

Choose a reason for hiding this comment

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

this seems to be a bit heavy on mem alocations especially for scenarios with multiple keys.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will try to refactor in order to make the code more efficient

Copy link
Contributor Author

Choose a reason for hiding this comment

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

refactored

func (cns *ConsensusState) IsMultiKeyJobDone(currentSubroundId int) bool {
managedKeys := cns.keysHolder.GetManagedKeysByCurrentNode()
for pk := range managedKeys {
if !cns.IsNodeInConsensusGroup(pk) {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe move the filtering of keys in consensus group managed by current node in a separate component, then we can optimize here allocations and number of iterations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, will try

Copy link
Contributor Author

Choose a reason for hiding this comment

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

refactored

@@ -168,11 +171,28 @@ func (sr *subroundStartRound) initCurrentRound() bool {

pubKeys := sr.ConsensusGroup()

numMultiKeysInConsensusGroup := 0
Copy link
Contributor

Choose a reason for hiding this comment

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

can you create a separate method for the added functionality here? L174-L188
then you can also add some unit tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, will add

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

return false
}

log.Debug("step 2: signature has been sent")
}
isSelfLeader := sr.IsSelfLeaderInCurrentRound()
Copy link
Contributor

Choose a reason for hiding this comment

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

can we do this on line 71 and then use it in the conditional and here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}

if !isAllInOneLeader {
cnsMsg := consensus.NewConsensusMessage(
Copy link
Contributor

Choose a reason for hiding this comment

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

this part looks very similar to L73-L118
can we extract some common functionality and use in both places?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, will try

Copy link
Contributor Author

Choose a reason for hiding this comment

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

refactored and eliminated duplicated code


keysHandler := &testscommon.KeysHandlerStub{}
cns := internalInitConsensusStateWithKeysHandler(keysHandler)
// managedKey := []byte("managed key")
Copy link
Contributor

Choose a reason for hiding this comment

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

commented code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

t.Parallel()

container := mock.InitConsensusCore()
keysHolder := &testscommon.KeysHandlerStub{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
keysHolder := &testscommon.KeysHandlerStub{}
keysHandler := &testscommon.KeysHandlerStub{}

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

&testscommon.KeysHolderStub{},
&testscommon.KeysHandlerStub{
IsOriginalPublicKeyOfTheNodeCalled: func(pkBytes []byte) bool {
return bytes.Equal(pkBytesProvided, pkBytes)
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use nodePkBytes you already defined and used in tests above and remove L268

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}

func initConsensusStateWithKeysHolder(keysHolder consensus.KeysHolder) *spos.ConsensusState {
func initConsensusStateWithKeysHandler(keysHolder consensus.KeysHandler) *spos.ConsensusState {
Copy link
Contributor

Choose a reason for hiding this comment

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

keysHandler

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}

return sr.doSignatureJobForManagedKeys()
}

func (sr *subroundSignature) createAndSendSignatureMessage(signatureShare []byte, pkBytes []byte) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

return true
}

func (sr *subroundSignature) completeSignatureSubRound(pk string, shouldWaitForAllSigsAsync bool) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

err = sr.BroadcastMessenger().BroadcastConsensusMessage(cnsMsg)
if err != nil {
log.Debug("doSignatureJob.BroadcastConsensusMessage", "error", err.Error())
numMultiKeysSignaturesSent++
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be moved to L364, when it is sure that the signature has been sent after execution with success of method createAndSendSignatureMessage

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, thanks

return false
}

// UpdatePublicKeyLiveness update the provided public key liveness if the provided pid is not managed by the current node
Copy link
Contributor

Choose a reason for hiding this comment

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

is not managed? or is managed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not managed. As you want to update the liveness of the pk when the master node did not participated in the consensus even if it should have.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok, now I see the sense the "not managed" is used ihere


// IsOriginalPublicKeyOfTheNode returns true if the provided public key bytes are the original ones used by the node
func (handler *keysHandler) IsOriginalPublicKeyOfTheNode(pkBytes []byte) bool {
return false
Copy link
Contributor

Choose a reason for hiding this comment

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

return true ? to work correctly in single-sign mode until the implementation will be done?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed

return stub.IsOriginalPublicKeyOfTheNodeCalled(pkBytes)
}

return false
Copy link
Contributor

Choose a reason for hiding this comment

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

return true ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, simplified one mock creation

@iulianpascalau iulianpascalau merged commit 7fe1d70 into feat/multisigner Sep 20, 2022
@iulianpascalau iulianpascalau deleted the consensus-multikey-integration branch September 20, 2022 09:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants