diff --git a/accounts/keys.go b/accounts/keys.go index e2a8e941..bc51ad1a 100644 --- a/accounts/keys.go +++ b/accounts/keys.go @@ -71,9 +71,14 @@ var _ Key = &KMSKey{} var _ Key = &BIP44Key{} +var _ Key = &EnvKey{} + 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 +320,44 @@ 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) { + return &f.privateKey, nil +} + +func (f *EnvKey) ToConfig() config.AccountKey { + return config.AccountKey{ + Type: config.KeyTypeHex, + SigAlgo: f.sigAlgo, + HashAlgo: f.hashAlgo, + PrivateKey: f.privateKey, + 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/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()) +}