diff --git a/validator/keymanager/wallet.go b/validator/keymanager/wallet.go index 25dfd40ca..532628f1b 100644 --- a/validator/keymanager/wallet.go +++ b/validator/keymanager/wallet.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "os" "regexp" "strings" @@ -55,6 +56,17 @@ func NewWallet(input string) (KeyManager, string, error) { if len(opts.Passphrases) == 0 { return nil, walletOptsHelp, errors.New("at least one passphrase is required to decrypt accounts") } + for i, passphrase := range opts.Passphrases { + if strings.HasPrefix(passphrase, "$") { + envPassphrase := os.Getenv(strings.TrimPrefix(passphrase, "$")) + if envPassphrase != "" { + // N.B. We do not log here if the environment variable is not found, as it is possible that this is actually a + // passphrase that just happens to begin with the '$' character. If this is a missing environment variable it will + // be noticed when the account fails to decrypt. + opts.Passphrases[i] = envPassphrase + } + } + } km := &Wallet{ accounts: make(map[[48]byte]e2wtypes.Account), diff --git a/validator/keymanager/wallet_test.go b/validator/keymanager/wallet_test.go index 4a695efad..5ce680da7 100644 --- a/validator/keymanager/wallet_test.go +++ b/validator/keymanager/wallet_test.go @@ -95,3 +95,76 @@ func TestMultiplePassphrases(t *testing.T) { }) } } + +func TestEnvPassphrases(t *testing.T) { + path := SetupWallet(t) + defer func() { + if err := os.RemoveAll(path); err != nil { + t.Log(err) + } + }() + + if err := os.Setenv("TESTENVPASSPHRASES_NEITHER", "neither"); err != nil { + t.Fatalf("Error setting environment variable TESTENVPASSPHRASES_NEITHER: %v", err) + } + defer func() { + if err := os.Unsetenv("TESTENVPASSPHRASES_NEITHER"); err != nil { + t.Fatalf("Error unsetting environment variable TESTENVPASSPHRASES_NEITHER: %v", err) + } + }() + if err := os.Setenv("TESTENVPASSPHRASES_FOO", "foo"); err != nil { + t.Fatalf("Error setting environment variable TESTENVPASSPHRASES_FOO: %v", err) + } + defer func() { + if err := os.Unsetenv("TESTENVPASSPHRASES_FOO"); err != nil { + t.Fatalf("Error unsetting environment variable TESTENVPASSPHRASES_FOO: %v", err) + } + }() + if err := os.Setenv("TESTENVPASSPHRASES_BAR", "bar"); err != nil { + t.Fatalf("Error setting environment variable TESTENVPASSPHRASES_BAR: %v", err) + } + defer func() { + if err := os.Unsetenv("TESTENVPASSPHRASES_BAR"); err != nil { + t.Fatalf("Error unsetting environment variable TESTENVPASSPHRASES_BAR: %v", err) + } + }() + + tests := []struct { + name string + wallet keymanager.KeyManager + accounts int + }{ + { + name: "Neither", + wallet: wallet(t, fmt.Sprintf(`{"location":%q,"accounts":["Wallet 1"],"passphrases":["$TESTENVPASSPHRASES_NEITHER"]}`, path)), + accounts: 0, + }, + { + name: "Foo", + wallet: wallet(t, fmt.Sprintf(`{"location":%q,"accounts":["Wallet 1"],"passphrases":["$TESTENVPASSPHRASES_FOO"]}`, path)), + accounts: 1, + }, + { + name: "Bar", + wallet: wallet(t, fmt.Sprintf(`{"location":%q,"accounts":["Wallet 1"],"passphrases":["$TESTENVPASSPHRASES_BAR"]}`, path)), + accounts: 1, + }, + { + name: "Both", + wallet: wallet(t, fmt.Sprintf(`{"location":%q,"accounts":["Wallet 1"],"passphrases":["$TESTENVPASSPHRASES_FOO","$TESTENVPASSPHRASES_BAR"]}`, path)), + accounts: 2, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + keys, err := test.wallet.FetchValidatingKeys() + if err != nil { + t.Error(err) + } + if len(keys) != test.accounts { + t.Errorf("Found %d keys; expected %d", len(keys), test.accounts) + } + }) + } +}