From 050a606566fc2780158bd9d057f1c5909311ee84 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 9 Dec 2024 14:53:18 -0800 Subject: [PATCH 1/5] Retain environment variable when loading key from config --- accounts/keys.go | 56 ++++++++++++++++++++++++++++++++++++++++++ config/json/account.go | 22 +++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/accounts/keys.go b/accounts/keys.go index e2a8e941..050c5802 100644 --- a/accounts/keys.go +++ b/accounts/keys.go @@ -74,6 +74,9 @@ var _ Key = &BIP44Key{} func keyFromConfig(accountKeyConf config.AccountKey) (Key, error) { switch accountKeyConf.Type { case config.KeyTypeHex: + if accountKeyConf.Env != "" { + return envKeyFromConfig(accountKeyConf) + } return hexKeyFromConfig(accountKeyConf) case config.KeyTypeBip44: return bip44KeyFromConfig(accountKeyConf) @@ -315,6 +318,59 @@ func NewFileKey( } } +// EnvKey represents a key that is saved in an environment variable. +type EnvKey struct { + *baseKey + privateKey crypto.PrivateKey + env string +} + +func envKeyFromConfig(accountKey config.AccountKey) (*EnvKey, error) { + return &EnvKey{ + baseKey: baseKeyFromConfig(accountKey), + privateKey: accountKey.PrivateKey, + env: accountKey.Env, + }, nil +} + +func (f *EnvKey) Signer(ctx context.Context) (crypto.Signer, error) { + key, err := f.PrivateKey() + if err != nil { + return nil, err + } + + return crypto.NewInMemorySigner(*key, f.HashAlgo()) +} + +func (f *EnvKey) PrivateKey() (*crypto.PrivateKey, error) { + pkHex := os.ExpandEnv(f.env) + if pkHex == "" { + return nil, fmt.Errorf("environment variable %s is not set", f.env) + } + + pkey, err := crypto.DecodePrivateKeyHex(f.sigAlgo, strings.TrimPrefix(string(pkHex), "0x")) + if err != nil { + return nil, fmt.Errorf("could not decode the key from environment variable %s: %w", f.env, err) + } + + return &pkey, nil +} + +func (f *EnvKey) ToConfig() config.AccountKey { + pk, err := f.PrivateKey() + if err != nil { + panic(err) + } + + return config.AccountKey{ + Type: config.KeyTypeHex, + SigAlgo: f.sigAlgo, + HashAlgo: f.hashAlgo, + PrivateKey: *pk, + Env: f.env, + } +} + // FileKey represents a key that is saved in a seperate file and will be lazy-loaded. // // The FileKey stores location of the file where private key is stored in hex-encoded format. diff --git a/config/json/account.go b/config/json/account.go index 708c8333..c1250f16 100644 --- a/config/json/account.go +++ b/config/json/account.go @@ -101,6 +101,25 @@ func transformSimpleToConfig(accountName string, a simpleAccount) (*config.Accou }, nil } +func isEnvKey(key string) bool { + envRegex, err := regexp.Compile(`^\$\{(\w+)\}|\$(\w+)$`) + if err != nil { + return false + } + + found := envRegex.FindAllStringSubmatch(key, -1) + if len(found) == 0 { + return false + } + + envVar := found[0][1] + if found[0][2] != "" { + envVar = found[0][2] + } + + return os.Getenv(envVar) != "" +} + // tryReplaceEnv checks if value matches env regex, if it does it check whether the value was set in env, // if not set then it errors, otherwise it replaces the value with set env variable, and also returns the original key. func tryReplaceEnv(value string) (replaced string, original string, err error) { @@ -277,6 +296,7 @@ func transformAccountsToJSON(accounts config.Accounts) jsonAccounts { func transformSimpleAccountToJSON(a config.Account) account { key := strings.TrimPrefix(a.Key.PrivateKey.String(), "0x") + if a.Key.Env != "" { key = a.Key.Env // if we used env vars then use it when saving } @@ -364,6 +384,8 @@ type advanceKey struct { Location string `json:"location,omitempty"` // old key format Context map[string]string `json:"context,omitempty"` + // env var + Env string `json:"env,omitempty"` } // support for pre v0.22 formats From 4759831d787e7b602bfd32ccd80f86defe658ee7 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 9 Dec 2024 15:04:38 -0800 Subject: [PATCH 2/5] fix ci --- config/json/account.go | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/config/json/account.go b/config/json/account.go index c1250f16..708c8333 100644 --- a/config/json/account.go +++ b/config/json/account.go @@ -101,25 +101,6 @@ func transformSimpleToConfig(accountName string, a simpleAccount) (*config.Accou }, nil } -func isEnvKey(key string) bool { - envRegex, err := regexp.Compile(`^\$\{(\w+)\}|\$(\w+)$`) - if err != nil { - return false - } - - found := envRegex.FindAllStringSubmatch(key, -1) - if len(found) == 0 { - return false - } - - envVar := found[0][1] - if found[0][2] != "" { - envVar = found[0][2] - } - - return os.Getenv(envVar) != "" -} - // tryReplaceEnv checks if value matches env regex, if it does it check whether the value was set in env, // if not set then it errors, otherwise it replaces the value with set env variable, and also returns the original key. func tryReplaceEnv(value string) (replaced string, original string, err error) { @@ -296,7 +277,6 @@ func transformAccountsToJSON(accounts config.Accounts) jsonAccounts { func transformSimpleAccountToJSON(a config.Account) account { key := strings.TrimPrefix(a.Key.PrivateKey.String(), "0x") - if a.Key.Env != "" { key = a.Key.Env // if we used env vars then use it when saving } @@ -384,8 +364,6 @@ type advanceKey struct { Location string `json:"location,omitempty"` // old key format Context map[string]string `json:"context,omitempty"` - // env var - Env string `json:"env,omitempty"` } // support for pre v0.22 formats From 753178b0f71da9811d50cf061192219d188d9279 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 9 Dec 2024 15:09:33 -0800 Subject: [PATCH 3/5] Add type guard --- accounts/keys.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accounts/keys.go b/accounts/keys.go index 050c5802..adac6c93 100644 --- a/accounts/keys.go +++ b/accounts/keys.go @@ -71,6 +71,8 @@ var _ Key = &KMSKey{} var _ Key = &BIP44Key{} +var _ Key = &EnvKey{} + func keyFromConfig(accountKeyConf config.AccountKey) (Key, error) { switch accountKeyConf.Type { case config.KeyTypeHex: From 333e272a4f7661dcbe52afbba6170fa3d75447ab Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 9 Dec 2024 15:24:47 -0800 Subject: [PATCH 4/5] Add test --- accounts/keys.go | 12 +----------- accounts/keys_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/accounts/keys.go b/accounts/keys.go index adac6c93..6b915968 100644 --- a/accounts/keys.go +++ b/accounts/keys.go @@ -345,17 +345,7 @@ func (f *EnvKey) Signer(ctx context.Context) (crypto.Signer, error) { } func (f *EnvKey) PrivateKey() (*crypto.PrivateKey, error) { - pkHex := os.ExpandEnv(f.env) - if pkHex == "" { - return nil, fmt.Errorf("environment variable %s is not set", f.env) - } - - pkey, err := crypto.DecodePrivateKeyHex(f.sigAlgo, strings.TrimPrefix(string(pkHex), "0x")) - if err != nil { - return nil, fmt.Errorf("could not decode the key from environment variable %s: %w", f.env, err) - } - - return &pkey, nil + return &f.privateKey, nil } func (f *EnvKey) ToConfig() config.AccountKey { diff --git a/accounts/keys_test.go b/accounts/keys_test.go index 0eb4ea9b..2713e524 100644 --- a/accounts/keys_test.go +++ b/accounts/keys_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flowkit/v2/tests" "github.com/onflow/flowkit/v2/config" @@ -89,3 +90,30 @@ func Test_BIP44(t *testing.T) { assert.NoError(t, err) assert.Equal(t, pubKey, sig.PublicKey().String()) } + +func Test_EnvKey(t *testing.T) { + pk, err := crypto.DecodePrivateKeyHex(config.DefaultSigAlgo, "64cfa38591cf755e84379d78884e5322af0fd2a94cff48569d6578cdd733d455") // TEST KEY DO NOT USE + assert.NoError(t, err) + + confKey := config.AccountKey{ + Type: config.KeyTypeHex, + Index: 0, + SigAlgo: config.DefaultSigAlgo, + HashAlgo: config.DefaultHashAlgo, + PrivateKey: pk, + Env: "TEST", + } + + key, err := envKeyFromConfig(confKey) + assert.NoError(t, err) + + const pubKey = "0x1e585ddefde564eb9d86c606a2cf33996c9434a4f658d7338a7b811e337adf6e38e2ae4a5c7a79751b5bf8b08a90428d0a29aa27e6ddc195099ac1b2deb9519a" + assert.Equal(t, confKey, key.ToConfig()) + pkey, err := key.PrivateKey() + assert.NoError(t, err) + assert.Equal(t, pubKey, (*pkey).PublicKey().String()) + + sig, err := key.Signer(context.Background()) + assert.NoError(t, err) + assert.Equal(t, pubKey, sig.PublicKey().String()) +} From a1b6bb22d1b2a557ae77b62ab23f345209fdeba8 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 9 Dec 2024 15:28:28 -0800 Subject: [PATCH 5/5] simplify --- accounts/keys.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/accounts/keys.go b/accounts/keys.go index 6b915968..bc51ad1a 100644 --- a/accounts/keys.go +++ b/accounts/keys.go @@ -349,16 +349,11 @@ func (f *EnvKey) PrivateKey() (*crypto.PrivateKey, error) { } func (f *EnvKey) ToConfig() config.AccountKey { - pk, err := f.PrivateKey() - if err != nil { - panic(err) - } - return config.AccountKey{ Type: config.KeyTypeHex, SigAlgo: f.sigAlgo, HashAlgo: f.hashAlgo, - PrivateKey: *pk, + PrivateKey: f.privateKey, Env: f.env, } }