github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/cmd/geth/accountcmd_test.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"os"
    21  	"path/filepath"
    22  	"runtime"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/cespare/cp"
    27  )
    28  
    29  // These tests are 'smoke tests' for the account related
    30  // subcommands and flags.
    31  //
    32  // For most tests, the test files from package accounts
    33  // are copied into a temporary keystore directory.
    34  
    35  func tmpDatadirWithKeystore(t *testing.T) string {
    36  	datadir := t.TempDir()
    37  	keystore := filepath.Join(datadir, "keystore")
    38  	source := filepath.Join("..", "..", "accounts", "keystore", "testdata", "keystore")
    39  	if err := cp.CopyAll(keystore, source); err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	return datadir
    43  }
    44  
    45  func TestAccountListEmpty(t *testing.T) {
    46  	t.Parallel()
    47  	geth := runGeth(t, "account", "list")
    48  	geth.ExpectExit()
    49  }
    50  
    51  func TestAccountList(t *testing.T) {
    52  	t.Parallel()
    53  	datadir := tmpDatadirWithKeystore(t)
    54  	var want = `
    55  Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
    56  Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa
    57  Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz
    58  `
    59  	if runtime.GOOS == "windows" {
    60  		want = `
    61  Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
    62  Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa
    63  Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz
    64  `
    65  	}
    66  	{
    67  		geth := runGeth(t, "account", "list", "--datadir", datadir)
    68  		geth.Expect(want)
    69  		geth.ExpectExit()
    70  	}
    71  	{
    72  		geth := runGeth(t, "--datadir", datadir, "account", "list")
    73  		geth.Expect(want)
    74  		geth.ExpectExit()
    75  	}
    76  }
    77  
    78  func TestAccountNew(t *testing.T) {
    79  	t.Parallel()
    80  	geth := runGeth(t, "account", "new", "--lightkdf")
    81  	defer geth.ExpectExit()
    82  	geth.Expect(`
    83  Your new account is locked with a password. Please give a password. Do not forget this password.
    84  !! Unsupported terminal, password will be echoed.
    85  Password: {{.InputLine "foobar"}}
    86  Repeat password: {{.InputLine "foobar"}}
    87  
    88  Your new key was generated
    89  `)
    90  	geth.ExpectRegexp(`
    91  Public address of the key:   0x[0-9a-fA-F]{40}
    92  Path of the secret key file: .*UTC--.+--[0-9a-f]{40}
    93  
    94  - You can share your public address with anyone. Others need it to interact with you.
    95  - You must NEVER share the secret key with anyone! The key controls access to your funds!
    96  - You must BACKUP your key file! Without the key, it's impossible to access account funds!
    97  - You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
    98  `)
    99  }
   100  
   101  func TestAccountImport(t *testing.T) {
   102  	t.Parallel()
   103  	tests := []struct{ name, key, output string }{
   104  		{
   105  			name:   "correct account",
   106  			key:    "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
   107  			output: "Address: {fcad0b19bb29d4674531d6f115237e16afce377c}\n",
   108  		},
   109  		{
   110  			name:   "invalid character",
   111  			key:    "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1",
   112  			output: "Fatal: Failed to load the private key: invalid character '1' at end of key file\n",
   113  		},
   114  	}
   115  	for _, test := range tests {
   116  		test := test
   117  		t.Run(test.name, func(t *testing.T) {
   118  			t.Parallel()
   119  			importAccountWithExpect(t, test.key, test.output)
   120  		})
   121  	}
   122  }
   123  
   124  func TestAccountHelp(t *testing.T) {
   125  	t.Parallel()
   126  	geth := runGeth(t, "account", "-h")
   127  	geth.WaitExit()
   128  	if have, want := geth.ExitStatus(), 0; have != want {
   129  		t.Errorf("exit error, have %d want %d", have, want)
   130  	}
   131  
   132  	geth = runGeth(t, "account", "import", "-h")
   133  	geth.WaitExit()
   134  	if have, want := geth.ExitStatus(), 0; have != want {
   135  		t.Errorf("exit error, have %d want %d", have, want)
   136  	}
   137  }
   138  
   139  func importAccountWithExpect(t *testing.T, key string, expected string) {
   140  	dir := t.TempDir()
   141  	keyfile := filepath.Join(dir, "key.prv")
   142  	if err := os.WriteFile(keyfile, []byte(key), 0600); err != nil {
   143  		t.Error(err)
   144  	}
   145  	passwordFile := filepath.Join(dir, "password.txt")
   146  	if err := os.WriteFile(passwordFile, []byte("foobar"), 0600); err != nil {
   147  		t.Error(err)
   148  	}
   149  	geth := runGeth(t, "--lightkdf", "account", "import", "-password", passwordFile, keyfile)
   150  	defer geth.ExpectExit()
   151  	geth.Expect(expected)
   152  }
   153  
   154  func TestAccountNewBadRepeat(t *testing.T) {
   155  	t.Parallel()
   156  	geth := runGeth(t, "account", "new", "--lightkdf")
   157  	defer geth.ExpectExit()
   158  	geth.Expect(`
   159  Your new account is locked with a password. Please give a password. Do not forget this password.
   160  !! Unsupported terminal, password will be echoed.
   161  Password: {{.InputLine "something"}}
   162  Repeat password: {{.InputLine "something else"}}
   163  Fatal: Passwords do not match
   164  `)
   165  }
   166  
   167  func TestAccountUpdate(t *testing.T) {
   168  	t.Parallel()
   169  	datadir := tmpDatadirWithKeystore(t)
   170  	geth := runGeth(t, "account", "update",
   171  		"--datadir", datadir, "--lightkdf",
   172  		"f466859ead1932d743d622cb74fc058882e8648a")
   173  	defer geth.ExpectExit()
   174  	geth.Expect(`
   175  Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
   176  !! Unsupported terminal, password will be echoed.
   177  Password: {{.InputLine "foobar"}}
   178  Please give a new password. Do not forget this password.
   179  Password: {{.InputLine "foobar2"}}
   180  Repeat password: {{.InputLine "foobar2"}}
   181  `)
   182  }
   183  
   184  func TestWalletImport(t *testing.T) {
   185  	t.Parallel()
   186  	geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
   187  	defer geth.ExpectExit()
   188  	geth.Expect(`
   189  !! Unsupported terminal, password will be echoed.
   190  Password: {{.InputLine "foo"}}
   191  Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
   192  `)
   193  
   194  	files, err := os.ReadDir(filepath.Join(geth.Datadir, "keystore"))
   195  	if len(files) != 1 {
   196  		t.Errorf("expected one key file in keystore directory, found %d files (error: %v)", len(files), err)
   197  	}
   198  }
   199  
   200  func TestWalletImportBadPassword(t *testing.T) {
   201  	t.Parallel()
   202  	geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
   203  	defer geth.ExpectExit()
   204  	geth.Expect(`
   205  !! Unsupported terminal, password will be echoed.
   206  Password: {{.InputLine "wrong"}}
   207  Fatal: could not decrypt key with given password
   208  `)
   209  }
   210  
   211  func TestUnlockFlag(t *testing.T) {
   212  	t.Parallel()
   213  	geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
   214  		"--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "console", "--exec", "loadScript('testdata/empty.js')")
   215  	geth.Expect(`
   216  Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
   217  !! Unsupported terminal, password will be echoed.
   218  Password: {{.InputLine "foobar"}}
   219  undefined
   220  `)
   221  	geth.ExpectExit()
   222  
   223  	wantMessages := []string{
   224  		"Unlocked account",
   225  		"=0xf466859eAD1932D743d622CB74FC058882E8648A",
   226  	}
   227  	for _, m := range wantMessages {
   228  		if !strings.Contains(geth.StderrText(), m) {
   229  			t.Errorf("stderr text does not contain %q", m)
   230  		}
   231  	}
   232  }
   233  
   234  func TestUnlockFlagWrongPassword(t *testing.T) {
   235  	t.Parallel()
   236  	geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
   237  		"--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "console", "--exec", "loadScript('testdata/empty.js')")
   238  
   239  	defer geth.ExpectExit()
   240  	geth.Expect(`
   241  Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
   242  !! Unsupported terminal, password will be echoed.
   243  Password: {{.InputLine "wrong1"}}
   244  Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3
   245  Password: {{.InputLine "wrong2"}}
   246  Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3
   247  Password: {{.InputLine "wrong3"}}
   248  Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given password)
   249  `)
   250  }
   251  
   252  // https://github.com/ethereum/go-ethereum/issues/1785
   253  func TestUnlockFlagMultiIndex(t *testing.T) {
   254  	t.Parallel()
   255  	geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
   256  		"--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--unlock", "0,2", "console", "--exec", "loadScript('testdata/empty.js')")
   257  
   258  	geth.Expect(`
   259  Unlocking account 0 | Attempt 1/3
   260  !! Unsupported terminal, password will be echoed.
   261  Password: {{.InputLine "foobar"}}
   262  Unlocking account 2 | Attempt 1/3
   263  Password: {{.InputLine "foobar"}}
   264  undefined
   265  `)
   266  	geth.ExpectExit()
   267  
   268  	wantMessages := []string{
   269  		"Unlocked account",
   270  		"=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8",
   271  		"=0x289d485D9771714CCe91D3393D764E1311907ACc",
   272  	}
   273  	for _, m := range wantMessages {
   274  		if !strings.Contains(geth.StderrText(), m) {
   275  			t.Errorf("stderr text does not contain %q", m)
   276  		}
   277  	}
   278  }
   279  
   280  func TestUnlockFlagPasswordFile(t *testing.T) {
   281  	t.Parallel()
   282  	geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
   283  		"--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--password", "testdata/passwords.txt", "--unlock", "0,2", "console", "--exec", "loadScript('testdata/empty.js')")
   284  
   285  	geth.Expect(`
   286  undefined
   287  `)
   288  	geth.ExpectExit()
   289  
   290  	wantMessages := []string{
   291  		"Unlocked account",
   292  		"=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8",
   293  		"=0x289d485D9771714CCe91D3393D764E1311907ACc",
   294  	}
   295  	for _, m := range wantMessages {
   296  		if !strings.Contains(geth.StderrText(), m) {
   297  			t.Errorf("stderr text does not contain %q", m)
   298  		}
   299  	}
   300  }
   301  
   302  func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) {
   303  	t.Parallel()
   304  	geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
   305  		"--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--password",
   306  		"testdata/wrong-passwords.txt", "--unlock", "0,2")
   307  	defer geth.ExpectExit()
   308  	geth.Expect(`
   309  Fatal: Failed to unlock account 0 (could not decrypt key with given password)
   310  `)
   311  }
   312  
   313  func TestUnlockFlagAmbiguous(t *testing.T) {
   314  	t.Parallel()
   315  	store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes")
   316  	geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
   317  		"--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--keystore",
   318  		store, "--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
   319  		"console", "--exec", "loadScript('testdata/empty.js')")
   320  	defer geth.ExpectExit()
   321  
   322  	// Helper for the expect template, returns absolute keystore path.
   323  	geth.SetTemplateFunc("keypath", func(file string) string {
   324  		abs, _ := filepath.Abs(filepath.Join(store, file))
   325  		return abs
   326  	})
   327  	geth.Expect(`
   328  Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
   329  !! Unsupported terminal, password will be echoed.
   330  Password: {{.InputLine "foobar"}}
   331  Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
   332     keystore://{{keypath "1"}}
   333     keystore://{{keypath "2"}}
   334  Testing your password against all of them...
   335  Your password unlocked keystore://{{keypath "1"}}
   336  In order to avoid this warning, you need to remove the following duplicate key files:
   337     keystore://{{keypath "2"}}
   338  undefined
   339  `)
   340  	geth.ExpectExit()
   341  
   342  	wantMessages := []string{
   343  		"Unlocked account",
   344  		"=0xf466859eAD1932D743d622CB74FC058882E8648A",
   345  	}
   346  	for _, m := range wantMessages {
   347  		if !strings.Contains(geth.StderrText(), m) {
   348  			t.Errorf("stderr text does not contain %q", m)
   349  		}
   350  	}
   351  }
   352  
   353  func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) {
   354  	t.Parallel()
   355  	store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes")
   356  	geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
   357  		"--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--keystore",
   358  		store, "--unlock", "f466859ead1932d743d622cb74fc058882e8648a")
   359  
   360  	defer geth.ExpectExit()
   361  
   362  	// Helper for the expect template, returns absolute keystore path.
   363  	geth.SetTemplateFunc("keypath", func(file string) string {
   364  		abs, _ := filepath.Abs(filepath.Join(store, file))
   365  		return abs
   366  	})
   367  	geth.Expect(`
   368  Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
   369  !! Unsupported terminal, password will be echoed.
   370  Password: {{.InputLine "wrong"}}
   371  Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
   372     keystore://{{keypath "1"}}
   373     keystore://{{keypath "2"}}
   374  Testing your password against all of them...
   375  Fatal: None of the listed files could be unlocked.
   376  `)
   377  	geth.ExpectExit()
   378  }