cmd/geth: add recovery procedure for AmbiguousAddrError
This commit is contained in:
1
accounts/testdata/dupes/1
vendored
Normal file
1
accounts/testdata/dupes/1
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
|
1
accounts/testdata/dupes/2
vendored
Normal file
1
accounts/testdata/dupes/2
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
|
1
accounts/testdata/dupes/foo
vendored
Normal file
1
accounts/testdata/dupes/foo
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3}
|
@ -181,10 +181,19 @@ func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i
|
|||||||
for trials := 0; trials < 3; trials++ {
|
for trials := 0; trials < 3; trials++ {
|
||||||
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
||||||
password := getPassPhrase(prompt, false, i, passwords)
|
password := getPassPhrase(prompt, false, i, passwords)
|
||||||
if err = accman.Unlock(account, password); err == nil {
|
err = accman.Unlock(account, password)
|
||||||
|
if err == nil {
|
||||||
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
|
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
|
||||||
return account, password
|
return account, password
|
||||||
}
|
}
|
||||||
|
if err, ok := err.(*accounts.AmbiguousAddrError); ok {
|
||||||
|
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
|
||||||
|
return ambiguousAddrRecovery(accman, err, password), password
|
||||||
|
}
|
||||||
|
if err != accounts.ErrDecrypt {
|
||||||
|
// No need to prompt again if the error is not decryption-related.
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// All trials expended to unlock account, bail out
|
// All trials expended to unlock account, bail out
|
||||||
utils.Fatalf("Failed to unlock account %s (%v)", address, err)
|
utils.Fatalf("Failed to unlock account %s (%v)", address, err)
|
||||||
@ -221,6 +230,32 @@ func getPassPhrase(prompt string, confirmation bool, i int, passwords []string)
|
|||||||
return password
|
return password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrError, auth string) accounts.Account {
|
||||||
|
fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
|
||||||
|
for _, a := range err.Matches {
|
||||||
|
fmt.Println(" ", a.File)
|
||||||
|
}
|
||||||
|
fmt.Println("Testing your passphrase against all of them...")
|
||||||
|
var match *accounts.Account
|
||||||
|
for _, a := range err.Matches {
|
||||||
|
if err := am.Unlock(a, auth); err == nil {
|
||||||
|
match = &a
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match == nil {
|
||||||
|
utils.Fatalf("None of the listed files could be unlocked.")
|
||||||
|
}
|
||||||
|
fmt.Printf("Your passphrase unlocked %s\n", match.File)
|
||||||
|
fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
|
||||||
|
for _, a := range err.Matches {
|
||||||
|
if a != *match {
|
||||||
|
fmt.Println(" ", a.File)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *match
|
||||||
|
}
|
||||||
|
|
||||||
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
||||||
func accountCreate(ctx *cli.Context) {
|
func accountCreate(ctx *cli.Context) {
|
||||||
accman := utils.MakeAccountManager(ctx)
|
accman := utils.MakeAccountManager(ctx)
|
||||||
|
@ -228,3 +228,65 @@ func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) {
|
|||||||
Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase)
|
Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase)
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnlockFlagAmbiguous(t *testing.T) {
|
||||||
|
store := filepath.Join("..", "..", "accounts", "testdata", "dupes")
|
||||||
|
geth := runGeth(t,
|
||||||
|
"--keystore", store, "--nat", "none", "--nodiscover", "--dev",
|
||||||
|
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
|
||||||
|
"js", "testdata/empty.js")
|
||||||
|
defer geth.expectExit()
|
||||||
|
|
||||||
|
// Helper for the expect template, returns absolute keystore path.
|
||||||
|
geth.setTemplateFunc("keypath", func(file string) string {
|
||||||
|
abs, _ := filepath.Abs(filepath.Join(store, file))
|
||||||
|
return abs
|
||||||
|
})
|
||||||
|
geth.expect(`
|
||||||
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "foobar"}}
|
||||||
|
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
|
||||||
|
{{keypath "1"}}
|
||||||
|
{{keypath "2"}}
|
||||||
|
Testing your passphrase against all of them...
|
||||||
|
Your passphrase unlocked {{keypath "1"}}
|
||||||
|
In order to avoid this warning, you need to remove the following duplicate key files:
|
||||||
|
{{keypath "2"}}
|
||||||
|
`)
|
||||||
|
geth.expectExit()
|
||||||
|
|
||||||
|
wantMessages := []string{
|
||||||
|
"Unlocked account f466859ead1932d743d622cb74fc058882e8648a",
|
||||||
|
}
|
||||||
|
for _, m := range wantMessages {
|
||||||
|
if strings.Index(geth.stderrText(), m) == -1 {
|
||||||
|
t.Errorf("stderr text does not contain %q", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) {
|
||||||
|
store := filepath.Join("..", "..", "accounts", "testdata", "dupes")
|
||||||
|
geth := runGeth(t,
|
||||||
|
"--keystore", store, "--nat", "none", "--nodiscover", "--dev",
|
||||||
|
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a")
|
||||||
|
defer geth.expectExit()
|
||||||
|
|
||||||
|
// Helper for the expect template, returns absolute keystore path.
|
||||||
|
geth.setTemplateFunc("keypath", func(file string) string {
|
||||||
|
abs, _ := filepath.Abs(filepath.Join(store, file))
|
||||||
|
return abs
|
||||||
|
})
|
||||||
|
geth.expect(`
|
||||||
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
|
!! Unsupported terminal, password will be echoed.
|
||||||
|
Passphrase: {{.InputLine "wrong"}}
|
||||||
|
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
|
||||||
|
{{keypath "1"}}
|
||||||
|
{{keypath "2"}}
|
||||||
|
Testing your passphrase against all of them...
|
||||||
|
Fatal: None of the listed files could be unlocked.
|
||||||
|
`)
|
||||||
|
geth.expectExit()
|
||||||
|
}
|
||||||
|
@ -45,6 +45,7 @@ type testgeth struct {
|
|||||||
// template variables for expect
|
// template variables for expect
|
||||||
Datadir string
|
Datadir string
|
||||||
Executable string
|
Executable string
|
||||||
|
Func template.FuncMap
|
||||||
|
|
||||||
removeDatadir bool
|
removeDatadir bool
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
@ -114,6 +115,13 @@ func (tt *testgeth) InputLine(s string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tt *testgeth) setTemplateFunc(name string, fn interface{}) {
|
||||||
|
if tt.Func == nil {
|
||||||
|
tt.Func = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
tt.Func[name] = fn
|
||||||
|
}
|
||||||
|
|
||||||
// expect runs its argument as a template, then expects the
|
// expect runs its argument as a template, then expects the
|
||||||
// child process to output the result of the template within 5s.
|
// child process to output the result of the template within 5s.
|
||||||
//
|
//
|
||||||
@ -121,7 +129,7 @@ func (tt *testgeth) InputLine(s string) string {
|
|||||||
// before matching.
|
// before matching.
|
||||||
func (tt *testgeth) expect(tplsource string) {
|
func (tt *testgeth) expect(tplsource string) {
|
||||||
// Generate the expected output by running the template.
|
// Generate the expected output by running the template.
|
||||||
tpl := template.Must(template.New("").Parse(tplsource))
|
tpl := template.Must(template.New("").Funcs(tt.Func).Parse(tplsource))
|
||||||
wantbuf := new(bytes.Buffer)
|
wantbuf := new(bytes.Buffer)
|
||||||
if err := tpl.Execute(wantbuf, tt); err != nil {
|
if err := tpl.Execute(wantbuf, tt); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
Reference in New Issue
Block a user