diff --git a/cmd/bootstrap/cmd/final_list.go b/cmd/bootstrap/cmd/final_list.go index 52db64980f9..f1a1e5b1901 100644 --- a/cmd/bootstrap/cmd/final_list.go +++ b/cmd/bootstrap/cmd/final_list.go @@ -242,7 +242,7 @@ func assembleInternalNodesWithoutWeight() []model.NodeInfo { common.ValidateAddressFormat(log, internal.Address) // validate every single internal node - nodeID := ValidateNodeID(internal.NodeID) + nodeID := common.ValidateNodeID(log, internal.NodeID) node := model.NewPrivateNodeInfo( nodeID, internal.Role, @@ -279,9 +279,9 @@ func createPublicNodeInfo(nodes []model.NodeInfoPub) []model.NodeInfo { common.ValidateAddressFormat(log, n.Address) // validate every single partner node - nodeID := ValidateNodeID(n.NodeID) - networkPubKey := ValidateNetworkPubKey(n.NetworkPubKey) - stakingPubKey := ValidateStakingPubKey(n.StakingPubKey) + nodeID := common.ValidateNodeID(log, n.NodeID) + networkPubKey := common.ValidateNetworkPubKey(log, n.NetworkPubKey) + stakingPubKey := common.ValidateStakingPubKey(log, n.StakingPubKey) // all nodes should have equal weight node := model.NewPublicNodeInfo( diff --git a/cmd/bootstrap/cmd/finalize.go b/cmd/bootstrap/cmd/finalize.go index ee4cce5e8c5..e4bf67fca96 100644 --- a/cmd/bootstrap/cmd/finalize.go +++ b/cmd/bootstrap/cmd/finalize.go @@ -20,7 +20,6 @@ import ( "github.com/onflow/flow-go/fvm" model "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/dkg" - "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/epochs" "github.com/onflow/flow-go/state/protocol" @@ -46,10 +45,7 @@ var ( flagGenesisTokenSupply string ) -// PartnerWeights is the format of the JSON file specifying partner node weights. -type PartnerWeights map[flow.Identifier]uint64 - -// finalizeCmd represents the finalize command +// finalizeCmd represents the finalize command` var finalizeCmd = &cobra.Command{ Use: "finalize", Short: "Finalize the bootstrapping process", @@ -357,31 +353,6 @@ func readDKGData() dkg.DKGData { // Validation utility methods ------------------------------------------------ -func ValidateNodeID(nodeID flow.Identifier) flow.Identifier { - if nodeID == flow.ZeroID { - log.Fatal().Msg("NodeID must not be zero") - } - return nodeID -} - -func ValidateNetworkPubKey(key encodable.NetworkPubKey) encodable.NetworkPubKey { - if key.PublicKey == nil { - log.Fatal().Msg("NetworkPubKey must not be nil") - } - return key -} - -func ValidateStakingPubKey(key encodable.StakingPubKey) encodable.StakingPubKey { - if key.PublicKey == nil { - log.Fatal().Msg("StakingPubKey must not be nil") - } - return key -} - -func ValidateWeight(weight uint64) (uint64, bool) { - return weight, weight > 0 -} - // loadRootProtocolSnapshot loads the root protocol snapshot from disk func loadRootProtocolSnapshot(path string) (*inmem.Snapshot, error) { data, err := io.ReadFile(filepath.Join(flagOutdir, path)) diff --git a/cmd/bootstrap/cmd/partner_infos.go b/cmd/bootstrap/cmd/partner_infos.go index d60fb7ac97e..68bee2a8430 100644 --- a/cmd/bootstrap/cmd/partner_infos.go +++ b/cmd/bootstrap/cmd/partner_infos.go @@ -64,7 +64,7 @@ func populatePartnerInfosRun(_ *cobra.Command, _ []string) { flowClient := getFlowClient() - partnerWeights := make(PartnerWeights) + partnerWeights := make(common.PartnerWeights) skippedNodes := 0 numOfPartnerNodesByRole := map[flow.Role]int{ flow.RoleCollection: 0, @@ -210,7 +210,7 @@ func writeNodePubInfoFile(info *bootstrap.NodeInfoPub) { } // writePartnerWeightsFile writes the partner weights file -func writePartnerWeightsFile(partnerWeights PartnerWeights) { +func writePartnerWeightsFile(partnerWeights common.PartnerWeights) { err := common.WriteJSON(bootstrap.FileNamePartnerWeights, flagOutdir, partnerWeights) if err != nil { log.Fatal().Err(err).Msg("failed to write json") diff --git a/cmd/util/cmd/common/clusters.go b/cmd/util/cmd/common/clusters.go index b8055acc2a1..4fe4c4347c8 100644 --- a/cmd/util/cmd/common/clusters.go +++ b/cmd/util/cmd/common/clusters.go @@ -2,9 +2,10 @@ package common import ( "errors" - + "fmt" "github.com/rs/zerolog" + "github.com/onflow/cadence" "github.com/onflow/flow-go/cmd/bootstrap/run" "github.com/onflow/flow-go/model/bootstrap" model "github.com/onflow/flow-go/model/bootstrap" @@ -13,6 +14,7 @@ import ( "github.com/onflow/flow-go/model/flow/assignment" "github.com/onflow/flow-go/model/flow/factory" "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/module/signature" ) // ConstructClusterAssignment random cluster assignment with internal and partner nodes. @@ -104,6 +106,43 @@ func ConstructRootQCsForClusters(log zerolog.Logger, clusterList flow.ClusterLis return qcs } +// ConvertClusterAssignmentsCdc converts golang cluster assignments type to cadence array of arrays. +func ConvertClusterAssignmentsCdc(assignments flow.AssignmentList) cadence.Array { + assignmentsCdc := make([]cadence.Value, len(assignments)) + for i, asmt := range assignments { + fmt.Println(asmt.Len()) + vals := make([]cadence.Value, asmt.Len()) + for j, k := range asmt { + fmt.Println(k.String()) + vals[j] = cadence.String(k.String()) + } + assignmentsCdc[i] = cadence.NewArray(vals).WithType(cadence.NewVariableSizedArrayType(cadence.StringType{})) + } + + return cadence.NewArray(assignmentsCdc).WithType(cadence.NewVariableSizedArrayType(cadence.NewVariableSizedArrayType(cadence.StringType{}))) +} + +// ConvertClusterQcsCdc converts golang cluster qcs type to cadence struct. +func ConvertClusterQcsCdc(qcs []*flow.QuorumCertificate, clusterList flow.ClusterList) ([]*flow.ClusterQCVoteData, error) { + voteData := make([]*flow.ClusterQCVoteData, len(qcs)) + for i, qc := range qcs { + c, ok := clusterList.ByIndex(uint(i)) + if !ok { + return nil, fmt.Errorf("could not get cluster list for cluster index %v", i) + } + voterIds, err := signature.DecodeSignerIndicesToIdentifiers(c.NodeIDs(), qc.SignerIndices) + if err != nil { + return nil, fmt.Errorf("could not decode signer indices: %w", err) + } + voteData[i] = &flow.ClusterQCVoteData{ + SigData: qc.SigData, + VoterIDs: voterIds, + } + } + + return voteData, nil +} + // Filters a list of nodes to include only nodes that will sign the QC for the // given cluster. The resulting list of nodes is only nodes that are in the // given cluster AND are not partner nodes (ie. we have the private keys). diff --git a/cmd/util/cmd/common/node_info.go b/cmd/util/cmd/common/node_info.go index 39aadafb578..2df112f2817 100644 --- a/cmd/util/cmd/common/node_info.go +++ b/cmd/util/cmd/common/node_info.go @@ -5,7 +5,6 @@ import ( "github.com/rs/zerolog" - "github.com/onflow/flow-go/cmd/bootstrap/cmd" "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/flow" ) @@ -16,7 +15,7 @@ func ReadPartnerNodeInfos(log zerolog.Logger, partnerWeightsPath, partnerNodeInf partners := ReadPartnerNodes(log, partnerNodeInfoDir) log.Info().Msgf("read %d partner node configuration files", len(partners)) - var weights cmd.PartnerWeights + var weights PartnerWeights err := ReadJSON(partnerWeightsPath, &weights) if err != nil { log.Fatal().Err(err).Msg("failed to read partner weights json") @@ -26,10 +25,10 @@ func ReadPartnerNodeInfos(log zerolog.Logger, partnerWeightsPath, partnerNodeInf var nodes []bootstrap.NodeInfo for _, partner := range partners { // validate every single partner node - nodeID := cmd.ValidateNodeID(partner.NodeID) - networkPubKey := cmd.ValidateNetworkPubKey(partner.NetworkPubKey) - stakingPubKey := cmd.ValidateStakingPubKey(partner.StakingPubKey) - weight, valid := cmd.ValidateWeight(weights[partner.NodeID]) + nodeID := ValidateNodeID(log, partner.NodeID) + networkPubKey := ValidateNetworkPubKey(log, partner.NetworkPubKey) + stakingPubKey := ValidateStakingPubKey(log, partner.StakingPubKey) + weight, valid := ValidateWeight(weights[partner.NodeID]) if !valid { log.Error().Msgf("weights: %v", weights) log.Fatal().Msgf("partner node id %x has no weight", nodeID) @@ -91,8 +90,8 @@ func ReadInternalNodeInfos(log zerolog.Logger, internalNodePrivInfoDir, internal ValidateAddressFormat(log, internal.Address) // validate every single internal node - nodeID := cmd.ValidateNodeID(internal.NodeID) - weight, valid := cmd.ValidateWeight(weights[internal.Address]) + nodeID := ValidateNodeID(log, internal.NodeID) + weight, valid := ValidateWeight(weights[internal.Address]) if !valid { log.Error().Msgf("weights: %v", weights) log.Fatal().Msgf("internal node %v has no weight. Did you forget to update the node address?", internal) diff --git a/cmd/util/cmd/common/utils.go b/cmd/util/cmd/common/utils.go index 2f2a8d03e90..a162feb4e65 100644 --- a/cmd/util/cmd/common/utils.go +++ b/cmd/util/cmd/common/utils.go @@ -8,11 +8,12 @@ import ( "path/filepath" "strconv" - "github.com/multiformats/go-multiaddr" "github.com/rs/zerolog" + "github.com/multiformats/go-multiaddr" "github.com/onflow/crypto" "github.com/onflow/flow-go/model/bootstrap" + "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/utils/io" @@ -128,3 +129,31 @@ func ValidateAddressFormat(log zerolog.Logger, address string) { _, err = multiaddr.NewMultiaddr(lp2pAddr) checkErr(err) } + +func ValidateNodeID(lg zerolog.Logger, nodeID flow.Identifier) flow.Identifier { + if nodeID == flow.ZeroID { + lg.Fatal().Msg("NodeID must not be zero") + } + return nodeID +} + +func ValidateNetworkPubKey(lg zerolog.Logger, key encodable.NetworkPubKey) encodable.NetworkPubKey { + if key.PublicKey == nil { + lg.Fatal().Msg("NetworkPubKey must not be nil") + } + return key +} + +func ValidateStakingPubKey(lg zerolog.Logger, key encodable.StakingPubKey) encodable.StakingPubKey { + if key.PublicKey == nil { + lg.Fatal().Msg("StakingPubKey must not be nil") + } + return key +} + +func ValidateWeight(weight uint64) (uint64, bool) { + return weight, weight > 0 +} + +// PartnerWeights is the format of the JSON file specifying partner node weights. +type PartnerWeights map[flow.Identifier]uint64 diff --git a/cmd/util/cmd/epochs/cmd/recover.go b/cmd/util/cmd/epochs/cmd/recover.go index 086534769fc..405d46473ee 100644 --- a/cmd/util/cmd/epochs/cmd/recover.go +++ b/cmd/util/cmd/epochs/cmd/recover.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "encoding/hex" "fmt" "github.com/spf13/cobra" @@ -20,10 +21,10 @@ import ( // identities, generates the cluster QC's and retrieves the DKG key vector of the last successful epoch. var ( generateRecoverEpochTxArgsCmd = &cobra.Command{ - Use: "generate-efm-recovery-data", + Use: "recover-epoch-tx-args", Short: "Generates recover epoch transaction arguments", Long: "Generates transaction arguments for the epoch recovery transaction.", - Run: generateRecoverEpochTxArgs, + Run: generateRecoverEpochTxArgs(getSnapshot), } flagAnAddress string @@ -31,7 +32,7 @@ var ( flagPartnerWeights string flagPartnerNodeInfoDir string flagInternalNodePrivInfoDir string - flagConfig string + flagNodeConfigJson string flagCollectionClusters int flagStartView uint64 flagStakingEndView uint64 @@ -44,12 +45,10 @@ func init() { } func addGenerateRecoverEpochTxArgsCmdFlags() { - generateRecoverEpochTxArgsCmd.Flags().StringVar(&flagBucketNetworkName, "bucket-network-name", "", - "when retrieving the root snapshot from a GCP bucket, the network name portion of the URL (eg. \"mainnet-13\")") generateRecoverEpochTxArgsCmd.Flags().IntVar(&flagCollectionClusters, "collection-clusters", 0, "number of collection clusters") // required parameters for network configuration and generation of root node identities - generateRecoverEpochTxArgsCmd.Flags().StringVar(&flagConfig, "config", "", + generateRecoverEpochTxArgsCmd.Flags().StringVar(&flagNodeConfigJson, "node-config", "", "path to a JSON file containing multiple node configurations (fields Role, Address, Weight)") generateRecoverEpochTxArgsCmd.Flags().StringVar(&flagInternalNodePrivInfoDir, "internal-priv-dir", "", "path to directory "+ "containing the output from the `keygen` command for internal nodes") @@ -63,10 +62,7 @@ func addGenerateRecoverEpochTxArgsCmdFlags() { generateRecoverEpochTxArgsCmd.Flags().Uint64Var(&flagEndView, "end-view", 0, "end view of the recovery epoch") } -// generateRecoverEpochTxArgs generates recover epoch transaction arguments from a root protocol state snapshot and writes it to a JSON file -func generateRecoverEpochTxArgs(cmd *cobra.Command, args []string) { - stdout := cmd.OutOrStdout() - +func getSnapshot() *inmem.Snapshot { // get flow client with secure client connection to download protocol snapshot from access node config, err := common.NewFlowClientConfig(flagAnAddress, flagAnPubkey, flow.ZeroID, false) if err != nil { @@ -83,19 +79,28 @@ func generateRecoverEpochTxArgs(cmd *cobra.Command, args []string) { log.Fatal().Err(err).Msg("failed") } - // extract arguments from recover epoch tx from snapshot - txArgs := extractRecoverEpochArgs(snapshot) + return snapshot +} - // encode to JSON - encodedTxArgs, err := epochcmdutil.EncodeArgs(txArgs) - if err != nil { - log.Fatal().Err(err).Msg("could not encode recover epoch transaction arguments") - } +// generateRecoverEpochTxArgs generates recover epoch transaction arguments from a root protocol state snapshot and writes it to a JSON file +func generateRecoverEpochTxArgs(getSnapshot func() *inmem.Snapshot) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + stdout := cmd.OutOrStdout() - // write JSON args to stdout - _, err = stdout.Write(encodedTxArgs) - if err != nil { - log.Fatal().Err(err).Msg("could not write jsoncdc encoded arguments") + // extract arguments from recover epoch tx from snapshot + txArgs := extractRecoverEpochArgs(getSnapshot()) + + // encode to JSON + encodedTxArgs, err := epochcmdutil.EncodeArgs(txArgs) + if err != nil { + log.Fatal().Err(err).Msg("could not encode recover epoch transaction arguments") + } + + // write JSON args to stdout + _, err = stdout.Write(encodedTxArgs) + if err != nil { + log.Fatal().Err(err).Msg("could not write jsoncdc encoded arguments") + } } } @@ -116,8 +121,8 @@ func extractRecoverEpochArgs(snapshot *inmem.Snapshot) []cadence.Value { partnerNodes := common.ReadPartnerNodeInfos(log, flagPartnerWeights, flagPartnerNodeInfoDir) log.Info().Msg("") - log.Info().Msg("generating internal private networking and staking keys") - internalNodes := common.ReadInternalNodeInfos(log, flagInternalNodePrivInfoDir, flagConfig) + log.Info().Msg("collecting internal node network and staking keys") + internalNodes := common.ReadInternalNodeInfos(log, flagInternalNodePrivInfoDir, flagNodeConfigJson) log.Info().Msg("") log.Info().Msg("computing collection node clusters") @@ -125,6 +130,7 @@ func extractRecoverEpochArgs(snapshot *inmem.Snapshot) []cadence.Value { if err != nil { log.Fatal().Err(err).Msg("unable to generate cluster assignment") } + log.Info().Msg("") epochCounter, err := epoch.Counter() @@ -137,14 +143,13 @@ func extractRecoverEpochArgs(snapshot *inmem.Snapshot) []cadence.Value { log.Info().Msg("constructing root QCs for collection node clusters") clusterQCs := common.ConstructRootQCsForClusters(log, clusters, internalNodes, clusterBlocks) - fmt.Sprintf("", clusterQCs) log.Info().Msg("") randomSource, err := epoch.RandomSource() if err != nil { log.Fatal().Err(err).Msg("failed to get random source for current epoch") } - randomSourceCdc, err := cadence.NewString(string(randomSource)) + randomSourceCdc, err := cadence.NewString(hex.EncodeToString(randomSource)) if err != nil { log.Fatal().Err(err).Msg("failed to get random source cadence string") } @@ -171,6 +176,14 @@ func extractRecoverEpochArgs(snapshot *inmem.Snapshot) []cadence.Value { return skeleton }) + // @TODO: cluster qcs are converted into flow.ClusterQCVoteData types, + // we need a corresponding type in cadence on the FlowClusterQC contract + // to store this struct. + _, err = common.ConvertClusterQcsCdc(clusterQCs, clusters) + if err != nil { + log.Fatal().Err(err).Msg("failed to convert cluster qcs to cadence type") + } + args := []cadence.Value{ randomSourceCdc, cadence.NewUInt64(flagStartView), @@ -178,8 +191,7 @@ func extractRecoverEpochArgs(snapshot *inmem.Snapshot) []cadence.Value { cadence.NewUInt64(flagEndView), cadence.NewArray(dkgPubKeys), cadence.NewArray(nodeIds), - // clusters - // clusterQcs + //common.ConvertClusterAssignmentsCdc(assignments), } return args diff --git a/cmd/util/cmd/epochs/cmd/recover_test.go b/cmd/util/cmd/epochs/cmd/recover_test.go new file mode 100644 index 00000000000..aa6efa9b17e --- /dev/null +++ b/cmd/util/cmd/epochs/cmd/recover_test.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/cmd/bootstrap/utils" + "github.com/onflow/flow-go/state/protocol/inmem" + "github.com/onflow/flow-go/utils/unittest" +) + +// TestRecoverEpochHappyPath ensures recover epoch transaction arguments are generated as expected. +func TestRecoverEpochHappyPath(t *testing.T) { + // tests that given the root snapshot, the command + // writes the expected arguments to stdout. + utils.RunWithSporkBootstrapDir(t, func(bootDir, partnerDir, partnerWeights, internalPrivDir, configPath string) { + // create a root snapshot + rootSnapshot := unittest.RootSnapshotFixture(unittest.IdentityListFixture(10, unittest.WithAllRoles())) + + snapshotFn := func() *inmem.Snapshot { return rootSnapshot } + + // run command with overwritten stdout + stdout := bytes.NewBuffer(nil) + generateRecoverEpochTxArgsCmd.SetOut(stdout) + + flagPartnerWeights = partnerWeights + flagPartnerNodeInfoDir = partnerDir + flagInternalNodePrivInfoDir = internalPrivDir + flagNodeConfigJson = configPath + flagCollectionClusters = 2 + flagStartView = 1000 + flagStakingEndView = 2000 + flagEndView = 4000 + + generateRecoverEpochTxArgs(snapshotFn)(generateRecoverEpochTxArgsCmd, nil) + + // read output from stdout + var outputTxArgs []interface{} + err := json.NewDecoder(stdout).Decode(&outputTxArgs) + require.NoError(t, err) + // compare to expected values + expectedArgs := extractRecoverEpochArgs(rootSnapshot) + + unittest.VerifyCdcArguments(t, expectedArgs, outputTxArgs) + }) +} diff --git a/cmd/util/cmd/epochs/cmd/reset_test.go b/cmd/util/cmd/epochs/cmd/reset_test.go index 25983e5cf61..30e7d0178f2 100644 --- a/cmd/util/cmd/epochs/cmd/reset_test.go +++ b/cmd/util/cmd/epochs/cmd/reset_test.go @@ -11,9 +11,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/onflow/cadence" - jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/utils/unittest" @@ -50,7 +47,7 @@ func TestReset_LocalSnapshot(t *testing.T) { // compare to expected values expectedArgs := extractResetEpochArgs(rootSnapshot) - verifyArguments(t, expectedArgs, outputTxArgs) + unittest.VerifyCdcArguments(t, expectedArgs, outputTxArgs) }) }) @@ -98,7 +95,7 @@ func TestReset_BucketSnapshot(t *testing.T) { rootSnapshot, err := getSnapshotFromBucket(fmt.Sprintf(rootSnapshotBucketURL, flagBucketNetworkName)) require.NoError(t, err) expectedArgs := extractResetEpochArgs(rootSnapshot) - verifyArguments(t, expectedArgs, outputTxArgs) + unittest.VerifyCdcArguments(t, expectedArgs, outputTxArgs) }) // should output arguments to stdout, including specified payout @@ -120,7 +117,7 @@ func TestReset_BucketSnapshot(t *testing.T) { rootSnapshot, err := getSnapshotFromBucket(fmt.Sprintf(rootSnapshotBucketURL, flagBucketNetworkName)) require.NoError(t, err) expectedArgs := extractResetEpochArgs(rootSnapshot) - verifyArguments(t, expectedArgs, outputTxArgs) + unittest.VerifyCdcArguments(t, expectedArgs, outputTxArgs) }) // with a missing snapshot, should log an error @@ -139,22 +136,6 @@ func TestReset_BucketSnapshot(t *testing.T) { }) } -func verifyArguments(t *testing.T, expected []cadence.Value, actual []interface{}) { - - for index, arg := range actual { - - // marshal to bytes - bz, err := json.Marshal(arg) - require.NoError(t, err) - - // parse cadence value - decoded, err := jsoncdc.Decode(nil, bz) - require.NoError(t, err) - - assert.Equal(t, expected[index], decoded) - } -} - func writeRootSnapshot(bootDir string, snapshot *inmem.Snapshot) error { rootSnapshotPath := filepath.Join(bootDir, bootstrap.PathRootProtocolStateSnapshot) return writeJSON(rootSnapshotPath, snapshot.Encodable()) diff --git a/utils/unittest/service_events_fixtures.go b/utils/unittest/service_events_fixtures.go index 0bd6f77e87c..7f4ad955d56 100644 --- a/utils/unittest/service_events_fixtures.go +++ b/utils/unittest/service_events_fixtures.go @@ -3,12 +3,17 @@ package unittest import ( "crypto/rand" "encoding/hex" + "encoding/json" + "testing" + + json2 "github.com/onflow/cadence/encoding/json" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" "github.com/onflow/cadence/runtime/common" "github.com/onflow/crypto" - "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/model/flow" ) @@ -537,7 +542,7 @@ func createEpochNodes() cadence.Array { func createEpochCollectors() cadence.Array { - clusterType := newFlowClusterQCClusterStructType() + clusterType := NewFlowClusterQCClusterStructType() voteType := newFlowClusterQCVoteStructType() @@ -699,76 +704,6 @@ func createVersionBeaconEvent() cadence.Event { }).WithType(NewNodeVersionBeaconVersionBeaconEventType()) } -func newFlowClusterQCVoteStructType() cadence.Type { - - // A.01cf0e2f2f715450.FlowClusterQC.Vote - - address, _ := common.HexToAddress("01cf0e2f2f715450") - location := common.NewAddressLocation(nil, address, "FlowClusterQC") - - return &cadence.StructType{ - Location: location, - QualifiedIdentifier: "FlowClusterQC.Vote", - Fields: []cadence.Field{ - { - Identifier: "nodeID", - Type: cadence.StringType{}, - }, - { - Identifier: "signature", - Type: cadence.NewOptionalType(cadence.StringType{}), - }, - { - Identifier: "message", - Type: cadence.NewOptionalType(cadence.StringType{}), - }, - { - Identifier: "clusterIndex", - Type: cadence.UInt16Type{}, - }, - { - Identifier: "weight", - Type: cadence.UInt64Type{}, - }, - }, - } -} - -func newFlowClusterQCClusterStructType() *cadence.StructType { - - // A.01cf0e2f2f715450.FlowClusterQC.Cluster - - address, _ := common.HexToAddress("01cf0e2f2f715450") - location := common.NewAddressLocation(nil, address, "FlowClusterQC") - - return &cadence.StructType{ - Location: location, - QualifiedIdentifier: "FlowClusterQC.Cluster", - Fields: []cadence.Field{ - { - Identifier: "index", - Type: cadence.UInt16Type{}, - }, - { - Identifier: "nodeWeights", - Type: cadence.NewDictionaryType(cadence.StringType{}, cadence.UInt64Type{}), - }, - { - Identifier: "totalWeight", - Type: cadence.UInt64Type{}, - }, - { - Identifier: "generatedVotes", - Type: cadence.NewDictionaryType(cadence.StringType{}, newFlowClusterQCVoteStructType()), - }, - { - Identifier: "uniqueVoteMessageTotalWeights", - Type: cadence.NewDictionaryType(cadence.StringType{}, cadence.UInt64Type{}), - }, - }, - } -} - func newFlowIDTableStakingNodeInfoStructType() *cadence.StructType { // A.01cf0e2f2f715450.FlowIDTableStaking.NodeInfo @@ -869,7 +804,7 @@ func newFlowEpochEpochSetupEventType() *cadence.EventType { }, { Identifier: "collectorClusters", - Type: cadence.NewVariableSizedArrayType(newFlowClusterQCClusterStructType()), + Type: cadence.NewVariableSizedArrayType(NewFlowClusterQCClusterStructType()), }, { Identifier: "randomSource", @@ -1098,3 +1033,89 @@ var VersionBeaconFixtureCCF = func() []byte { } return b }() + +func newFlowClusterQCVoteStructType() *cadence.StructType { + + // A.01cf0e2f2f715450.FlowClusterQC.Vote + + address, _ := common.HexToAddress("01cf0e2f2f715450") + location := common.NewAddressLocation(nil, address, "FlowClusterQC") + + return &cadence.StructType{ + Location: location, + QualifiedIdentifier: "FlowClusterQC.Vote", + Fields: []cadence.Field{ + { + Identifier: "nodeID", + Type: cadence.StringType{}, + }, + { + Identifier: "signature", + Type: cadence.NewOptionalType(cadence.StringType{}), + }, + { + Identifier: "message", + Type: cadence.NewOptionalType(cadence.StringType{}), + }, + { + Identifier: "clusterIndex", + Type: cadence.UInt16Type{}, + }, + { + Identifier: "weight", + Type: cadence.UInt64Type{}, + }, + }, + } +} + +func VerifyCdcArguments(t *testing.T, expected []cadence.Value, actual []interface{}) { + + for index, arg := range actual { + + // marshal to bytes + bz, err := json.Marshal(arg) + require.NoError(t, err) + + // parse cadence value + decoded, err := json2.Decode(nil, bz) + require.NoError(t, err) + + assert.Equal(t, expected[index], decoded) + } +} + +func NewFlowClusterQCClusterStructType() *cadence.StructType { + + // A.01cf0e2f2f715450.FlowClusterQC.Cluster + + address, _ := common.HexToAddress("01cf0e2f2f715450") + location := common.NewAddressLocation(nil, address, "FlowClusterQC") + + return &cadence.StructType{ + Location: location, + QualifiedIdentifier: "FlowClusterQC.Cluster", + Fields: []cadence.Field{ + { + Identifier: "index", + Type: cadence.UInt16Type{}, + }, + { + Identifier: "nodeWeights", + Type: cadence.NewDictionaryType(cadence.StringType{}, cadence.UInt64Type{}), + }, + { + Identifier: "totalWeight", + Type: cadence.UInt64Type{}, + }, + { + Identifier: "generatedVotes", + Type: cadence.NewDictionaryType(cadence.StringType{}, newFlowClusterQCVoteStructType()), + }, + { + Identifier: "uniqueVoteMessageTotalWeights", + Type: cadence.NewDictionaryType(cadence.StringType{}, cadence.UInt64Type{}), + }, + }, + } +}