package initialize import ( "encoding/hex" "fmt" "os" "path/filepath" "strconv" "testing" "time" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags" cmdConfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/config" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/generate" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/netmap" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/node" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/policy" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/spf13/viper" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) const ( contractsPath = "../../../../../../contract/frostfs-contract-v0.18.0.tar.gz" protoFileName = "proto.yml" ) func TestInitialize(t *testing.T) { // This test needs frostfs-contract tarball, so it is skipped by default. // It is here for performing local testing after the changes. t.Skip() t.Run("1 nodes", func(t *testing.T) { testInitialize(t, 1) }) t.Run("4 nodes", func(t *testing.T) { testInitialize(t, 4) }) t.Run("7 nodes", func(t *testing.T) { testInitialize(t, 7) }) t.Run("16 nodes", func(t *testing.T) { testInitialize(t, 16) }) t.Run("max nodes", func(t *testing.T) { testInitialize(t, constants.MaxAlphabetNodes) }) t.Run("too many nodes", func(t *testing.T) { require.ErrorIs(t, generateTestData(t.TempDir(), constants.MaxAlphabetNodes+1), helper.ErrTooManyAlphabetNodes) }) } func testInitialize(t *testing.T, committeeSize int) { testdataDir := t.TempDir() v := viper.GetViper() require.NoError(t, generateTestData(testdataDir, committeeSize)) v.Set(constants.ProtoConfigPath, filepath.Join(testdataDir, protoFileName)) // Set to the path or remove the next statement to download from the network. require.NoError(t, Cmd.Flags().Set(commonflags.ContractsInitFlag, contractsPath)) dumpPath := filepath.Join(testdataDir, "out") require.NoError(t, Cmd.Flags().Set(commonflags.LocalDumpFlag, dumpPath)) v.Set(commonflags.AlphabetWalletsFlag, testdataDir) v.Set(commonflags.EpochDurationInitFlag, 1) v.Set(commonflags.MaxObjectSizeInitFlag, 1024) setTestCredentials(v, committeeSize) require.NoError(t, initializeSideChainCmd(Cmd, nil)) t.Run("force-new-epoch", func(t *testing.T) { require.NoError(t, netmap.ForceNewEpoch.Flags().Set(commonflags.LocalDumpFlag, dumpPath)) require.NoError(t, netmap.ForceNewEpochCmd(netmap.ForceNewEpoch, nil)) }) t.Run("set-config", func(t *testing.T) { require.NoError(t, cmdConfig.SetCmd.Flags().Set(commonflags.LocalDumpFlag, dumpPath)) require.NoError(t, cmdConfig.SetConfigCmd(cmdConfig.SetCmd, []string{"MaintenanceModeAllowed=true"})) }) t.Run("set-policy", func(t *testing.T) { require.NoError(t, policy.Set.Flags().Set(commonflags.LocalDumpFlag, dumpPath)) require.NoError(t, policy.SetPolicyCmd(policy.Set, []string{"ExecFeeFactor=1"})) }) t.Run("remove-node", func(t *testing.T) { pk, err := keys.NewPrivateKey() require.NoError(t, err) pub := hex.EncodeToString(pk.PublicKey().Bytes()) require.NoError(t, node.RemoveCmd.Flags().Set(commonflags.LocalDumpFlag, dumpPath)) require.NoError(t, node.RemoveNodesCmd(node.RemoveCmd, []string{pub})) }) } func generateTestData(dir string, size int) error { v := viper.GetViper() v.Set(commonflags.AlphabetWalletsFlag, dir) sizeStr := strconv.FormatUint(uint64(size), 10) if err := generate.GenerateAlphabetCmd.Flags().Set(commonflags.AlphabetSizeFlag, sizeStr); err != nil { return err } setTestCredentials(v, size) if err := generate.AlphabetCreds(generate.GenerateAlphabetCmd, nil); err != nil { return err } var pubs []string for i := 0; i < size; i++ { p := filepath.Join(dir, innerring.GlagoliticLetter(i).String()+".json") w, err := wallet.NewWalletFromFile(p) if err != nil { return fmt.Errorf("wallet doesn't exist: %w", err) } for _, acc := range w.Accounts { if acc.Label == constants.SingleAccountName { pub, ok := vm.ParseSignatureContract(acc.Contract.Script) if !ok { return fmt.Errorf("could not parse signature script for %s", acc.Address) } pubs = append(pubs, hex.EncodeToString(pub)) continue } } } cfg := config.Config{} cfg.ProtocolConfiguration.Magic = 12345 cfg.ProtocolConfiguration.ValidatorsCount = uint32(size) cfg.ProtocolConfiguration.TimePerBlock = time.Second cfg.ProtocolConfiguration.StandbyCommittee = pubs // sorted by glagolic letters cfg.ProtocolConfiguration.P2PSigExtensions = true cfg.ProtocolConfiguration.VerifyTransactions = true data, err := yaml.Marshal(cfg) if err != nil { return err } protoPath := filepath.Join(dir, protoFileName) return os.WriteFile(protoPath, data, os.ModePerm) } func setTestCredentials(v *viper.Viper, size int) { for i := 0; i < size; i++ { v.Set("credentials."+innerring.GlagoliticLetter(i).String(), strconv.FormatUint(uint64(i), 10)) } v.Set("credentials.contract", constants.TestContractPassword) }