Skip to content
Merged
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
29 changes: 29 additions & 0 deletions internal/settings/multisig_validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package settings

import (
"fmt"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// IsMultisigMode reports whether multisig transaction mode is active via --unsigned or --changeset.
func IsMultisigMode(v *viper.Viper) bool {
return v.GetBool(Flags.RawTxFlag.Name) ||
v.GetBool(Flags.Changeset.Name)
}

// ValidateMultisigCompatibility rejects incompatible multisig, private registry, and browser secrets auth combinations.
// resolvedRegistry may be nil during initial settings load; the private-registry check is skipped until resolved.
func ValidateMultisigCompatibility(v *viper.Viper, cmd *cobra.Command, resolvedRegistry ResolvedRegistry) error {
if !IsMultisigMode(v) {
return nil
}
if f := cmd.Flags().Lookup("secrets-auth"); f != nil && f.Value.String() == "browser" {
return fmt.Errorf("browser secrets auth cannot be combined with multisig secrets operations; remove --unsigned/--changeset or use --secrets-auth=onchain")
}
if resolvedRegistry != nil && resolvedRegistry.Type() == RegistryTypeOffChain {
return fmt.Errorf("multisig operations (--unsigned or --changeset) are not supported with private registry; remove the flag or use an on-chain deployment-registry")
}
return nil
}
84 changes: 84 additions & 0 deletions internal/settings/multisig_validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package settings

import (
"testing"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestIsMultisigMode(t *testing.T) {
t.Run("unsigned", func(t *testing.T) {
v := viper.New()
v.Set(Flags.RawTxFlag.Name, true)
assert.True(t, IsMultisigMode(v))
})

t.Run("changeset", func(t *testing.T) {
v := viper.New()
v.Set(Flags.Changeset.Name, true)
assert.True(t, IsMultisigMode(v))
})

t.Run("neither flag", func(t *testing.T) {
v := viper.New()
assert.False(t, IsMultisigMode(v))
})
}

func secretsCmd(secretsAuth string) *cobra.Command {
cmd := &cobra.Command{Use: "create"}
cmd.Flags().String("secrets-auth", secretsAuth, "auth mode")
return cmd
}

func workflowCmd() *cobra.Command {
return &cobra.Command{Use: "deploy"}
}

func TestValidateMultisigCompatibility(t *testing.T) {
offChain := NewOffChainRegistry("42", "test-don")
onChain := NewOnChainRegistry("mainnet", "0xabc", "ethereum-mainnet", "test-don", "")

tests := []struct {
name string
unsigned bool
changeset bool
cmd *cobra.Command
resolvedRegistry ResolvedRegistry
wantErr bool
errMsg string
}{
{"not multisig secrets onchain off-chain", false, false, secretsCmd("onchain"), offChain, false, ""},
{"not multisig secrets browser off-chain", false, false, secretsCmd("browser"), offChain, false, ""},
{"unsigned secrets onchain on-chain", true, false, secretsCmd("onchain"), onChain, false, ""},
{"unsigned secrets onchain nil registry", true, false, secretsCmd("onchain"), nil, false, ""},
{"changeset secrets browser off-chain", false, true, secretsCmd("browser"), offChain, true, "browser secrets auth cannot be combined with multisig"},
{"unsigned secrets browser on-chain", true, false, secretsCmd("browser"), onChain, true, "browser secrets auth cannot be combined with multisig"},
{"unsigned secrets onchain off-chain", true, false, secretsCmd("onchain"), offChain, true, "not supported with private registry"},
{"changeset workflow off-chain", false, true, workflowCmd(), offChain, true, "not supported with private registry"},
{"unsigned workflow on-chain", true, false, workflowCmd(), onChain, false, ""},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := viper.New()
if tt.unsigned {
v.Set(Flags.RawTxFlag.Name, true)
}
if tt.changeset {
v.Set(Flags.Changeset.Name, true)
}

err := ValidateMultisigCompatibility(v, tt.cmd, tt.resolvedRegistry)
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), tt.errMsg)
} else {
require.NoError(t, err)
}
})
}
}
7 changes: 7 additions & 0 deletions internal/settings/workflow_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ func loadWorkflowSettings(logger *zerolog.Logger, v *viper.Viper, cmd *cobra.Com
return WorkflowSettings{}, errors.Wrap(err, "for target "+target)
}

if err := ValidateMultisigCompatibility(v, cmd, nil); err != nil {
return WorkflowSettings{}, err
}

// This is required because some commands still read values directly out of viper
// TODO: Remove this function once all access to settings no longer uses viper
// DEVSVCS-1561
Expand All @@ -187,6 +191,9 @@ func FinalizeWorkflowOwner(
resolved ResolvedRegistry,
derivedWorkflowOwner string,
) error {
if err := ValidateMultisigCompatibility(v, cmd, resolved); err != nil {
return err
}
if ShouldSkipGetOwner(cmd) {
return nil
}
Expand Down
Loading