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