Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,6 @@ func isLoadCredentials(cmd *cobra.Command) bool {
"cre account": {},
"cre secrets": {},
"cre workflow build": {},
"cre workflow hash": {},
"cre templates": {},
"cre templates list": {},
"cre templates add": {},
Expand Down
23 changes: 23 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,26 @@ func TestIsRegistryRPCCommand(t *testing.T) {
})
}
}

func TestIsLoadCredentials(t *testing.T) {
t.Parallel()

tests := []struct {
name string
path []string
wantLoad bool
}{
{"workflow hash", []string{"cre", "workflow", "hash"}, true},
{"workflow deploy", []string{"cre", "workflow", "deploy"}, true},
{"login", []string{"cre", "login"}, false},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
cmd := buildCommandPath(tc.path...)
assert.Equal(t, tc.wantLoad, isLoadCredentials(cmd),
"isLoadCredentials(%q)", cmd.CommandPath())
})
}
}
31 changes: 23 additions & 8 deletions cmd/workflow/hash/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type Inputs struct {
OwnerFromSettings string
PrivateKey string
SkipTypeChecks bool
RegistryType settings.RegistryType
DerivedOwner string
}

func New(runtimeContext *runtime.Context) *cobra.Command {
Expand All @@ -41,6 +43,10 @@ func New(runtimeContext *runtime.Context) *cobra.Command {
v := runtimeContext.Viper

rawPrivKey := v.GetString(settings.EthPrivateKeyEnvVar)
registryType := settings.RegistryTypeOnChain
if runtimeContext.ResolvedRegistry != nil {
registryType = runtimeContext.ResolvedRegistry.Type()
}

inputs := Inputs{
ForUser: forUser,
Expand All @@ -51,6 +57,8 @@ func New(runtimeContext *runtime.Context) *cobra.Command {
OwnerFromSettings: s.Workflow.UserWorkflowSettings.WorkflowOwnerAddress,
PrivateKey: settings.NormalizeHexKey(rawPrivKey),
SkipTypeChecks: v.GetBool(cmdcommon.SkipTypeChecksCLIFlag),
RegistryType: registryType,
DerivedOwner: runtimeContext.DerivedWorkflowOwner,
}

return Execute(inputs)
Expand Down Expand Up @@ -87,7 +95,7 @@ func Execute(inputs Inputs) error {
return err
}

ownerAddress, err := ResolveOwner(inputs.ForUser, inputs.OwnerFromSettings, inputs.PrivateKey)
ownerAddress, err := ResolveOwner(inputs)
if err != nil {
return err
}
Expand All @@ -107,17 +115,24 @@ func Execute(inputs Inputs) error {
return nil
}

func ResolveOwner(forUser, ownerFromSettings, privateKey string) (string, error) {
if forUser != "" {
return forUser, nil
func ResolveOwner(inputs Inputs) (string, error) {
if inputs.RegistryType == settings.RegistryTypeOffChain {
if inputs.DerivedOwner == "" {
return "", fmt.Errorf("derived workflow owner is not available; ensure authentication succeeded")
}
return inputs.DerivedOwner, nil
}

if inputs.ForUser != "" {
return inputs.ForUser, nil
}

if ownerFromSettings != "" {
return ownerFromSettings, nil
if inputs.OwnerFromSettings != "" {
return inputs.OwnerFromSettings, nil
}

if privateKey != "" {
addr, err := ethkeys.DeriveEthAddressFromPrivateKey(privateKey)
if inputs.PrivateKey != "" {
addr, err := ethkeys.DeriveEthAddressFromPrivateKey(inputs.PrivateKey)
if err != nil {
return "", fmt.Errorf("failed to derive owner from private key: %w", err)
}
Expand Down
51 changes: 45 additions & 6 deletions cmd/workflow/hash/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
workflowUtils "github.com/smartcontractkit/chainlink-common/pkg/workflows"

cmdcommon "github.com/smartcontractkit/cre-cli/cmd/common"
"github.com/smartcontractkit/cre-cli/internal/settings"
)

// Well-known test private key (never use on a real network).
Expand All @@ -25,47 +26,79 @@ const testDerivedAddress = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"

func TestResolveOwner_WithForUser(t *testing.T) {
t.Parallel()
addr, err := ResolveOwner("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF", "", "")
inputs := resolveInputs()
inputs.ForUser = "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
addr, err := ResolveOwner(inputs)
require.NoError(t, err)
assert.Equal(t, "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF", addr)
}

func TestResolveOwner_WithForUserOverridesAll(t *testing.T) {
t.Parallel()
addr, err := ResolveOwner("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF", "0xOtherAddress", testPrivateKey)
inputs := resolveInputs()
inputs.ForUser = "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
inputs.OwnerFromSettings = "0xOtherAddress"
inputs.PrivateKey = testPrivateKey
addr, err := ResolveOwner(inputs)
require.NoError(t, err)
assert.Equal(t, "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF", addr,
"--public_key should take priority over settings and private key")
}

func TestResolveOwner_FromSettings(t *testing.T) {
t.Parallel()
addr, err := ResolveOwner("", "0xSettingsOwner", "")
inputs := resolveInputs()
inputs.OwnerFromSettings = "0xSettingsOwner"
addr, err := ResolveOwner(inputs)
require.NoError(t, err)
assert.Equal(t, "0xSettingsOwner", addr)
}

func TestResolveOwner_FromPrivateKey(t *testing.T) {
t.Parallel()
addr, err := ResolveOwner("", "", testPrivateKey)
inputs := resolveInputs()
inputs.PrivateKey = testPrivateKey
addr, err := ResolveOwner(inputs)
require.NoError(t, err)
assert.Equal(t, testDerivedAddress, addr)
}

func TestResolveOwner_NothingProvided(t *testing.T) {
t.Parallel()
_, err := ResolveOwner("", "", "")
_, err := ResolveOwner(resolveInputs())
require.Error(t, err)
assert.Contains(t, err.Error(), "--public_key")
}

func TestResolveOwner_InvalidPrivateKey(t *testing.T) {
t.Parallel()
_, err := ResolveOwner("", "", "not-a-valid-key")
inputs := resolveInputs()
inputs.PrivateKey = "not-a-valid-key"
_, err := ResolveOwner(inputs)
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to derive owner")
}

func TestResolveOwner_OffChainUsesDerivedOwner(t *testing.T) {
t.Parallel()
inputs := resolveInputs()
inputs.RegistryType = settings.RegistryTypeOffChain
inputs.DerivedOwner = "0xDerivedOwner"
inputs.ForUser = "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
addr, err := ResolveOwner(inputs)
require.NoError(t, err)
assert.Equal(t, "0xDerivedOwner", addr)
}

func TestResolveOwner_OffChainMissingDerivedOwner(t *testing.T) {
t.Parallel()
inputs := resolveInputs()
inputs.RegistryType = settings.RegistryTypeOffChain
_, err := ResolveOwner(inputs)
require.Error(t, err)
assert.Contains(t, err.Error(), "derived workflow owner is not available")
}

func TestExecute_WithForUser(t *testing.T) {
wasmFile, configFile := setupTestArtifacts(t)

Expand Down Expand Up @@ -230,6 +263,12 @@ func TestHashCommandFlags(t *testing.T) {
require.NotNil(t, f, "no-config flag should exist")
}

func resolveInputs() Inputs {
return Inputs{
RegistryType: settings.RegistryTypeOnChain,
}
}

// setupTestArtifacts creates a minimal WASM file and config file in a temp directory.
func setupTestArtifacts(t *testing.T) (wasmPath, configPath string) {
t.Helper()
Expand Down
Loading