Skip to content

Commit 6fcd492

Browse files
authored
Don't create machine accounts for non-consensus or collection nodes (#244)
* generate quorum certificate even if a node hasn't submitted * dont create machine account for non collection or consensus
1 parent 0c51d55 commit 6fcd492

File tree

7 files changed

+189
-16
lines changed

7 files changed

+189
-16
lines changed

contracts/FlowStakingCollection.cdc

+7-1
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,15 @@ pub contract FlowStakingCollection {
407407
let nodeReference = self.borrowNode(id)
408408
?? panic("Could not borrow node reference")
409409

410+
let nodeInfo = FlowIDTableStaking.NodeInfo(nodeID: nodeReference.id)
411+
410412
// Register the machine account for the node
411413
// creates an auth account object and returns it to the caller
412-
return self.registerMachineAccount(nodeReference: nodeReference, payer: payer)
414+
if nodeInfo.role == FlowEpoch.NodeRole.Collector.rawValue || nodeInfo.role == FlowEpoch.NodeRole.Consensus.rawValue {
415+
return self.registerMachineAccount(nodeReference: nodeReference, payer: payer)
416+
} else {
417+
return nil
418+
}
413419
}
414420

415421
/// Registers the secondary machine account for a node

contracts/epochs/FlowClusterQC.cdc

+7-3
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,13 @@ pub contract FlowClusterQC {
149149
// Add the signatures, messages, and node IDs only for votes
150150
// that match the votes that reached quorum
151151
for vote in self.generatedVotes.values {
152-
if vote.message! == quorumMessage {
153-
certificate.voteSignatures.append(vote.signature!)
154-
certificate.voterIDs.append(vote.nodeID)
152+
153+
// Only count votes that were submitted
154+
if let submittedMessage = vote.message {
155+
if submittedMessage == quorumMessage {
156+
certificate.voteSignatures.append(vote.signature!)
157+
certificate.voterIDs.append(vote.nodeID)
158+
}
155159
}
156160
}
157161

lib/go/contracts/internal/assets/assets.go

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/go/templates/flow_qc_templates.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ const (
2727
getVotingCompletedFilename = "quorumCertificate/scripts/get_voting_completed.cdc"
2828
getClusterVotesFilename = "quorumCertificate/scripts/get_cluster_votes.cdc"
2929

30-
getClusterVoteThresholdFilename = "quorumCertificate/scripts/get_cluster_vote_threshold.cdc"
31-
getClusterWeightFilename = "quorumCertificate/scripts/get_cluster_weight.cdc"
32-
getClusterNodeWeightsFilename = "quorumCertificate/scripts/get_cluster_node_weights.cdc"
33-
getNodeWeightFilename = "quorumCertificate/scripts/get_node_weight.cdc"
34-
getVoterIsRegisteredFilename = "quorumCertificate/scripts/get_voter_is_registered.cdc"
35-
getNodeHasVotedFilename = "quorumCertificate/scripts/get_node_has_voted.cdc"
30+
getClusterVoteThresholdFilename = "quorumCertificate/scripts/get_cluster_vote_threshold.cdc"
31+
getClusterWeightFilename = "quorumCertificate/scripts/get_cluster_weight.cdc"
32+
getClusterNodeWeightsFilename = "quorumCertificate/scripts/get_cluster_node_weights.cdc"
33+
getNodeWeightFilename = "quorumCertificate/scripts/get_node_weight.cdc"
34+
getVoterIsRegisteredFilename = "quorumCertificate/scripts/get_voter_is_registered.cdc"
35+
getNodeHasVotedFilename = "quorumCertificate/scripts/get_node_has_voted.cdc"
36+
generateQuorumCertificateFilename = "quorumCertificate/scripts/generate_quorum_certificate.cdc"
3637
)
3738

3839
// Admin Templates -----------------------------------------------------------
@@ -146,3 +147,9 @@ func GenerateGetNodeHasVotedScript(env Environment) []byte {
146147

147148
return []byte(replaceAddresses(code, env))
148149
}
150+
151+
func GenerateGenerateQuorumCertificateScript(env Environment) []byte {
152+
code := assets.MustAssetString(generateQuorumCertificateFilename)
153+
154+
return []byte(replaceAddresses(code, env))
155+
}

lib/go/templates/internal/assets/assets.go

+23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/go/test/flow_qc_test.go

+121
Original file line numberDiff line numberDiff line change
@@ -723,3 +723,124 @@ func TestQuorumCertificateMoreNodes(t *testing.T) {
723723
assert.Equal(t, cadence.NewBool(true), result)
724724
})
725725
}
726+
727+
func TestQuorumCertificateNotSubmittedVote(t *testing.T) {
728+
b := newBlockchain()
729+
730+
env := templates.Environment{
731+
FungibleTokenAddress: emulatorFTAddress,
732+
FlowTokenAddress: emulatorFlowTokenAddress,
733+
}
734+
735+
accountKeys := test.AccountKeyGenerator()
736+
737+
// Create new keys for the QC account and deploy
738+
QCAccountKey, QCSigner := accountKeys.NewWithSigner()
739+
QCCode := contracts.FlowQC()
740+
741+
QCAddress, err := b.CreateAccount([]*flow.AccountKey{QCAccountKey}, []sdktemplates.Contract{
742+
{
743+
Name: "FlowClusterQC",
744+
Source: string(QCCode),
745+
},
746+
})
747+
if !assert.NoError(t, err) {
748+
t.Log(err.Error())
749+
}
750+
751+
env.QuorumCertificateAddress = QCAddress.Hex()
752+
753+
collectorVoteHasher := flow_crypto.NewBLSKMAC(encoding.CollectorVoteTag)
754+
755+
tx := createTxWithTemplateAndAuthorizer(b, templates.GeneratePublishVoterScript(env), QCAddress)
756+
757+
signAndSubmit(
758+
t, b, tx,
759+
[]flow.Address{b.ServiceKey().Address, QCAddress},
760+
[]crypto.Signer{b.ServiceKey().Signer(), QCSigner},
761+
false,
762+
)
763+
764+
numberOfClusters := 1
765+
numberOfNodesPerCluster := 4
766+
767+
// Create new user accounts
768+
addresses, _, signers := registerAndMintManyAccounts(t, b, accountKeys, numberOfClusters*numberOfNodesPerCluster)
769+
770+
clusterNodeIDStrings := make([][]string, numberOfClusters*numberOfNodesPerCluster)
771+
772+
stakingPrivateKeys, stakingPublicKeys, _, _ := generateManyNodeKeys(t, numberOfClusters*numberOfNodesPerCluster)
773+
774+
// initializes clusters by filling them all in in order
775+
// Other tests continue this cluster organization assumption
776+
startVotingArguments := initClusters(clusterNodeIDStrings, numberOfClusters, numberOfNodesPerCluster)
777+
778+
tx = createTxWithTemplateAndAuthorizer(b, templates.GenerateStartVotingScript(env), QCAddress)
779+
780+
err = tx.AddArgument(cadence.NewArray(startVotingArguments[0]))
781+
require.NoError(t, err)
782+
783+
err = tx.AddArgument(cadence.NewArray(startVotingArguments[1]))
784+
require.NoError(t, err)
785+
786+
err = tx.AddArgument(cadence.NewArray(startVotingArguments[2]))
787+
require.NoError(t, err)
788+
789+
signAndSubmit(
790+
t, b, tx,
791+
[]flow.Address{b.ServiceKey().Address, QCAddress},
792+
[]crypto.Signer{b.ServiceKey().Signer(), QCSigner},
793+
false,
794+
)
795+
796+
// Claim voter resources
797+
for i := 0; i < numberOfClusters*numberOfNodesPerCluster; i++ {
798+
799+
tx := createTxWithTemplateAndAuthorizer(b, templates.GenerateCreateVoterScript(env), addresses[i])
800+
801+
_ = tx.AddArgument(cadence.NewAddress(QCAddress))
802+
_ = tx.AddArgument(cadence.NewString(clusterNodeIDStrings[i/numberOfNodesPerCluster][i%numberOfNodesPerCluster]))
803+
_ = tx.AddArgument(cadence.NewString(stakingPublicKeys[i]))
804+
805+
signAndSubmit(
806+
t, b, tx,
807+
[]flow.Address{b.ServiceKey().Address, addresses[i]},
808+
[]crypto.Signer{b.ServiceKey().Signer(), signers[i]},
809+
false,
810+
)
811+
}
812+
813+
t.Run("Should generate a valid quorum certificate even if a node hasn't voted", func(t *testing.T) {
814+
815+
for i := 0; i < numberOfNodesPerCluster-1; i++ {
816+
817+
// Construct a valid message and signature
818+
msg, _ := hex.DecodeString("deadbeef")
819+
validSignature, err := stakingPrivateKeys[i].Sign(msg, collectorVoteHasher)
820+
validSignatureString := validSignature.String()[2:]
821+
assert.NoError(t, err)
822+
823+
tx := createTxWithTemplateAndAuthorizer(b, templates.GenerateSubmitVoteScript(env), addresses[i])
824+
825+
_ = tx.AddArgument(cadence.NewString(validSignatureString))
826+
_ = tx.AddArgument(cadence.NewString("deadbeef"))
827+
828+
signAndSubmit(
829+
t, b, tx,
830+
[]flow.Address{b.ServiceKey().Address, addresses[i]},
831+
[]crypto.Signer{b.ServiceKey().Signer(), signers[i]},
832+
false,
833+
)
834+
}
835+
836+
result := executeScriptAndCheck(t, b, templates.GenerateGetVotingCompletedScript(env), nil)
837+
assert.Equal(t, cadence.NewBool(true), result)
838+
839+
result = executeScriptAndCheck(t, b, templates.GenerateGetClusterCompleteScript(env), [][]byte{jsoncdc.MustEncode(cadence.UInt16(uint16(0)))})
840+
assert.Equal(t, cadence.NewBool(true), result)
841+
842+
executeScriptAndCheck(t, b, templates.GenerateGenerateQuorumCertificateScript(env), [][]byte{jsoncdc.MustEncode(cadence.UInt16(uint16(0)))})
843+
844+
})
845+
846+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import FlowClusterQC from 0xQCADDRESS
2+
3+
// Gets the status of a cluster's QC generation
4+
5+
pub fun main(clusterIndex: UInt16): FlowClusterQC.ClusterQC {
6+
7+
let clusters = FlowClusterQC.getClusters()
8+
9+
return clusters[clusterIndex].generateQuorumCertificate()
10+
?? panic("Could not generate quorum certificate")
11+
12+
}

0 commit comments

Comments
 (0)