github.com/Finschia/finschia-sdk@v0.48.1/client/keys/add_test.go (about)

     1  package keys
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"testing"
     9  
    10  	"github.com/cosmos/go-bip39"
    11  	"github.com/spf13/pflag"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/Finschia/ostracon/libs/cli"
    15  
    16  	"github.com/Finschia/finschia-sdk/client"
    17  	"github.com/Finschia/finschia-sdk/client/flags"
    18  	"github.com/Finschia/finschia-sdk/crypto/hd"
    19  	"github.com/Finschia/finschia-sdk/crypto/keyring"
    20  	"github.com/Finschia/finschia-sdk/simapp"
    21  	"github.com/Finschia/finschia-sdk/testutil"
    22  	"github.com/Finschia/finschia-sdk/testutil/testdata"
    23  	sdk "github.com/Finschia/finschia-sdk/types"
    24  )
    25  
    26  func Test_runAddCmdBasic(t *testing.T) {
    27  	cmd := AddKeyCommand()
    28  	cmd.Flags().AddFlagSet(Commands("home").PersistentFlags())
    29  
    30  	mockIn := testutil.ApplyMockIODiscardOutErr(cmd)
    31  	kbHome := t.TempDir()
    32  
    33  	kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn)
    34  	require.NoError(t, err)
    35  
    36  	clientCtx := client.Context{}.WithKeyringDir(kbHome).WithInput(mockIn).WithKeyring(kb)
    37  	clientCtxPtr := &clientCtx
    38  	ctx := context.WithValue(context.Background(), client.ClientContextKey, clientCtxPtr)
    39  
    40  	t.Cleanup(func() {
    41  		_ = kb.Delete("keyname1")
    42  		_ = kb.Delete("keyname2")
    43  	})
    44  
    45  	cmd.SetArgs([]string{
    46  		"keyname1",
    47  		fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
    48  		fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
    49  		fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
    50  		fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
    51  	})
    52  	mockIn.Reset("y\n")
    53  	require.NoError(t, cmd.ExecuteContext(ctx))
    54  
    55  	mockIn.Reset("N\n")
    56  	require.Error(t, cmd.ExecuteContext(ctx))
    57  
    58  	cmd.SetArgs([]string{
    59  		"keyname2",
    60  		fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
    61  		fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
    62  		fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
    63  		fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
    64  	})
    65  
    66  	require.NoError(t, cmd.ExecuteContext(ctx))
    67  
    68  	mockIn.Reset("y\n")
    69  	require.NoError(t, cmd.ExecuteContext(ctx))
    70  
    71  	cmd.SetArgs([]string{
    72  		"keyname4",
    73  		fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
    74  		fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
    75  		fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
    76  		fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
    77  	})
    78  
    79  	require.NoError(t, cmd.ExecuteContext(ctx))
    80  
    81  	// In Multisig
    82  	tcs := []struct {
    83  		args []string
    84  		err  string
    85  	}{
    86  		{[]string{
    87  			"keyname1",
    88  			fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
    89  			fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
    90  			fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
    91  			fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
    92  			fmt.Sprintf("--%s=%s", flagMultisig, "keyname1,keyname2"),
    93  		},
    94  			"you cannot specify a new key as one of the names of the keys that make up a multisig",
    95  		},
    96  		{[]string{
    97  			"keyname-multi",
    98  			fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
    99  			fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
   100  			fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
   101  			fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
   102  			fmt.Sprintf("--%s=%s", flagMultisig, "keyname1,keyname11"),
   103  		},
   104  			"part of the multisig target key does not exist",
   105  		},
   106  		{[]string{
   107  			"keyname-multi",
   108  			fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
   109  			fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
   110  			fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
   111  			fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
   112  			fmt.Sprintf("--%s=%s", flagMultisig, "keyname1,keyname2"),
   113  			fmt.Sprintf("--%s=%d", flagMultiSigThreshold, 3),
   114  		},
   115  			"threshold k of n multisignature",
   116  		},
   117  		{[]string{
   118  			"keyname-multi",
   119  			fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
   120  			fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
   121  			fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
   122  			fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
   123  			fmt.Sprintf("--%s=%s", flagMultisig, "keyname1,keyname2"),
   124  			fmt.Sprintf("--%s=%d", flagMultiSigThreshold, -1),
   125  		},
   126  			"threshold must be a positive integer",
   127  		},
   128  		{[]string{
   129  			"keyname-multi",
   130  			fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
   131  			fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
   132  			fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
   133  			fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
   134  			fmt.Sprintf("--%s=%s", flagMultisig, "keyname1,keyname2"),
   135  			fmt.Sprintf("--%s=%d", flagMultiSigThreshold, 2),
   136  		},
   137  			"",
   138  		},
   139  	}
   140  
   141  	for _, tc := range tcs {
   142  		cmd.SetArgs(tc.args)
   143  		if tc.err != "" {
   144  			require.Contains(t, cmd.ExecuteContext(ctx).Error(), tc.err)
   145  		} else {
   146  			require.NoError(t, cmd.ExecuteContext(ctx))
   147  		}
   148  
   149  		cmd.Flags().Visit(func(f *pflag.Flag) {
   150  			if f.Name == flagMultisig {
   151  				f.Value.(pflag.SliceValue).Replace([]string{})
   152  			}
   153  		})
   154  	}
   155  
   156  	cmd.SetArgs([]string{
   157  		"keyname5",
   158  		fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
   159  		fmt.Sprintf("--%s=true", flags.FlagDryRun),
   160  		fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
   161  		fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
   162  	})
   163  	mockIn.Reset("\n")
   164  	require.NoError(t, cmd.ExecuteContext(ctx))
   165  
   166  	// In recovery mode
   167  	cmd.SetArgs([]string{
   168  		"keyname6",
   169  		fmt.Sprintf("--%s=true", flagRecover),
   170  	})
   171  
   172  	// use valid mnemonic and complete recovery key generation successfully
   173  	mockIn.Reset("decide praise business actor peasant farm drastic weather extend front hurt later song give verb rhythm worry fun pond reform school tumble august one\n")
   174  	require.NoError(t, cmd.ExecuteContext(ctx))
   175  
   176  	// use invalid mnemonic and fail recovery key generation
   177  	mockIn.Reset("invalid mnemonic\n")
   178  	require.Error(t, cmd.ExecuteContext(ctx))
   179  
   180  	// In interactive mode
   181  	cmd.SetArgs([]string{
   182  		"keyname7",
   183  		"-i",
   184  		fmt.Sprintf("--%s=false", flagRecover),
   185  	})
   186  
   187  	const password = "password1!"
   188  
   189  	// set password and complete interactive key generation successfully
   190  	mockIn.Reset("\n" + password + "\n" + password + "\n")
   191  	require.NoError(t, cmd.ExecuteContext(ctx))
   192  
   193  	// passwords don't match and fail interactive key generation
   194  	mockIn.Reset("\n" + password + "\n" + "fail" + "\n")
   195  	require.Error(t, cmd.ExecuteContext(ctx))
   196  }
   197  
   198  func TestAddRecoverFileBackend(t *testing.T) {
   199  	cmd := AddKeyCommand()
   200  	cmd.Flags().AddFlagSet(Commands("home").PersistentFlags())
   201  
   202  	mockIn := testutil.ApplyMockIODiscardOutErr(cmd)
   203  	kbHome := t.TempDir()
   204  
   205  	clientCtx := client.Context{}.WithKeyringDir(kbHome).WithInput(mockIn)
   206  	ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx)
   207  
   208  	cmd.SetArgs([]string{
   209  		"keyname1",
   210  		fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
   211  		fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText),
   212  		fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)),
   213  		fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendFile),
   214  		fmt.Sprintf("--%s", flagRecover),
   215  	})
   216  
   217  	keyringPassword := "12345678"
   218  
   219  	entropySeed, err := bip39.NewEntropy(mnemonicEntropySize)
   220  	require.NoError(t, err)
   221  
   222  	mnemonic, err := bip39.NewMnemonic(entropySeed)
   223  	require.NoError(t, err)
   224  
   225  	mockIn.Reset(fmt.Sprintf("%s\n%s\n%s\n", mnemonic, keyringPassword, keyringPassword))
   226  	require.NoError(t, cmd.ExecuteContext(ctx))
   227  
   228  	kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendFile, kbHome, mockIn)
   229  	require.NoError(t, err)
   230  
   231  	t.Cleanup(func() {
   232  		mockIn.Reset(fmt.Sprintf("%s\n%s\n", keyringPassword, keyringPassword))
   233  		_ = kb.Delete("keyname1")
   234  	})
   235  
   236  	mockIn.Reset(fmt.Sprintf("%s\n%s\n", keyringPassword, keyringPassword))
   237  	info, err := kb.Key("keyname1")
   238  	require.NoError(t, err)
   239  	require.Equal(t, "keyname1", info.GetName())
   240  }
   241  
   242  func Test_runAddCmdDryRun(t *testing.T) {
   243  	pubkey1 := `{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtObiFVE4s+9+RX5SP8TN9r2mxpoaT4eGj9CJfK7VRzN"}`
   244  	pubkey2 := `{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A/se1vkqgdQ7VJQCM4mxN+L+ciGhnnJ4XYsQCRBMrdRi"}`
   245  
   246  	testData := []struct {
   247  		name  string
   248  		args  []string
   249  		added bool
   250  	}{
   251  		{
   252  			name: "account is added",
   253  			args: []string{
   254  				"testkey",
   255  				fmt.Sprintf("--%s=%s", flags.FlagDryRun, "false"),
   256  			},
   257  			added: true,
   258  		},
   259  		{
   260  			name: "account is not added with dry run",
   261  			args: []string{
   262  				"testkey",
   263  				fmt.Sprintf("--%s=%s", flags.FlagDryRun, "true"),
   264  			},
   265  			added: false,
   266  		},
   267  		{
   268  			name: "multisig account is added",
   269  			args: []string{
   270  				"testkey",
   271  				fmt.Sprintf("--%s=%s", flags.FlagDryRun, "false"),
   272  				fmt.Sprintf("--%s=%s", flagMultisig, "subkey"),
   273  			},
   274  			added: true,
   275  		},
   276  		{
   277  			name: "multisig account is not added with dry run",
   278  			args: []string{
   279  				"testkey",
   280  				fmt.Sprintf("--%s=%s", flags.FlagDryRun, "true"),
   281  				fmt.Sprintf("--%s=%s", flagMultisig, "subkey"),
   282  			},
   283  			added: false,
   284  		},
   285  		{
   286  			name: "pubkey account is added",
   287  			args: []string{
   288  				"testkey",
   289  				fmt.Sprintf("--%s=%s", flags.FlagDryRun, "false"),
   290  				fmt.Sprintf("--%s=%s", FlagPublicKey, pubkey1),
   291  			},
   292  			added: true,
   293  		},
   294  		{
   295  			name: "pubkey account is not added with dry run",
   296  			args: []string{
   297  				"testkey",
   298  				fmt.Sprintf("--%s=%s", flags.FlagDryRun, "true"),
   299  				fmt.Sprintf("--%s=%s", FlagPublicKey, pubkey2),
   300  			},
   301  			added: false,
   302  		},
   303  	}
   304  	for _, tt := range testData {
   305  		tt := tt
   306  		t.Run(tt.name, func(t *testing.T) {
   307  			cmd := AddKeyCommand()
   308  			cmd.Flags().AddFlagSet(Commands("home").PersistentFlags())
   309  
   310  			kbHome := t.TempDir()
   311  			mockIn := testutil.ApplyMockIODiscardOutErr(cmd)
   312  			kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn)
   313  			require.NoError(t, err)
   314  
   315  			appCodec := simapp.MakeTestEncodingConfig().Marshaler
   316  			clientCtx := client.Context{}.
   317  				WithJSONCodec(appCodec).
   318  				WithKeyringDir(kbHome).
   319  				WithKeyring(kb)
   320  			ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx)
   321  
   322  			path := sdk.GetConfig().GetFullBIP44Path()
   323  			_, err = kb.NewAccount("subkey", testdata.TestMnemonic, "", path, hd.Secp256k1)
   324  			require.NoError(t, err)
   325  
   326  			t.Cleanup(func() {
   327  				_ = kb.Delete("subkey")
   328  			})
   329  
   330  			b := bytes.NewBufferString("")
   331  			cmd.SetOut(b)
   332  
   333  			cmd.SetArgs(tt.args)
   334  			require.NoError(t, cmd.ExecuteContext(ctx))
   335  
   336  			if tt.added {
   337  				_, err = kb.Key("testkey")
   338  				require.NoError(t, err)
   339  
   340  				out, err := io.ReadAll(b)
   341  				require.NoError(t, err)
   342  				require.Contains(t, string(out), "name: testkey")
   343  			} else {
   344  				_, err = kb.Key("testkey")
   345  				require.Error(t, err)
   346  				require.Equal(t, "testkey.info: key not found", err.Error())
   347  			}
   348  		})
   349  	}
   350  }