github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/keys/client/add_multisig.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"flag"
     7  	"fmt"
     8  	"sort"
     9  
    10  	"github.com/gnolang/gno/tm2/pkg/commands"
    11  	"github.com/gnolang/gno/tm2/pkg/crypto"
    12  	"github.com/gnolang/gno/tm2/pkg/crypto/keys"
    13  	"github.com/gnolang/gno/tm2/pkg/crypto/multisig"
    14  )
    15  
    16  var (
    17  	errOverwriteAborted       = errors.New("overwrite aborted")
    18  	errUnableToVerifyMultisig = errors.New("unable to verify multisig threshold")
    19  )
    20  
    21  type AddMultisigCfg struct {
    22  	RootCfg *AddCfg
    23  
    24  	NoSort            bool
    25  	Multisig          commands.StringArr
    26  	MultisigThreshold int
    27  }
    28  
    29  // NewAddMultisigCmd creates a gnokey add multisig command
    30  func NewAddMultisigCmd(rootCfg *AddCfg, io commands.IO) *commands.Command {
    31  	cfg := &AddMultisigCfg{
    32  		RootCfg: rootCfg,
    33  	}
    34  
    35  	return commands.NewCommand(
    36  		commands.Metadata{
    37  			Name:       "multisig",
    38  			ShortUsage: "add multisig [flags] <key-name>",
    39  			ShortHelp:  "adds a multisig key reference to the keybase",
    40  		},
    41  		cfg,
    42  		func(_ context.Context, args []string) error {
    43  			return execAddMultisig(cfg, args, io)
    44  		},
    45  	)
    46  }
    47  
    48  func (c *AddMultisigCfg) RegisterFlags(fs *flag.FlagSet) {
    49  	fs.BoolVar(
    50  		&c.NoSort,
    51  		"nosort",
    52  		false,
    53  		"keys passed to --multisig are taken in the order they're supplied",
    54  	)
    55  
    56  	fs.Var(
    57  		&c.Multisig,
    58  		"multisig",
    59  		"construct and store a multisig public key",
    60  	)
    61  
    62  	fs.IntVar(
    63  		&c.MultisigThreshold,
    64  		"threshold",
    65  		1,
    66  		"K out of N required signatures",
    67  	)
    68  }
    69  
    70  func execAddMultisig(cfg *AddMultisigCfg, args []string, io commands.IO) error {
    71  	// Validate a key name was provided
    72  	if len(args) != 1 {
    73  		return flag.ErrHelp
    74  	}
    75  
    76  	// Validate the multisig threshold
    77  	if err := keys.ValidateMultisigThreshold(
    78  		cfg.MultisigThreshold,
    79  		len(cfg.Multisig),
    80  	); err != nil {
    81  		return errUnableToVerifyMultisig
    82  	}
    83  
    84  	name := args[0]
    85  
    86  	// Read the keybase from the home directory
    87  	kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home)
    88  	if err != nil {
    89  		return fmt.Errorf("unable to read keybase, %w", err)
    90  	}
    91  
    92  	// Check if the key exists
    93  	exists, err := kb.HasByName(name)
    94  	if err != nil {
    95  		return fmt.Errorf("unable to fetch key, %w", err)
    96  	}
    97  
    98  	// Get overwrite confirmation, if any
    99  	if exists {
   100  		overwrite, err := io.GetConfirmation(fmt.Sprintf("Override the existing name %s", name))
   101  		if err != nil {
   102  			return fmt.Errorf("unable to get confirmation, %w", err)
   103  		}
   104  
   105  		if !overwrite {
   106  			return errOverwriteAborted
   107  		}
   108  	}
   109  
   110  	publicKeys := make([]crypto.PubKey, 0)
   111  	for _, keyName := range cfg.Multisig {
   112  		k, err := kb.GetByName(keyName)
   113  		if err != nil {
   114  			return fmt.Errorf("unable to fetch key, %w", err)
   115  		}
   116  
   117  		publicKeys = append(publicKeys, k.GetPubKey())
   118  	}
   119  
   120  	// Check if the keys should be sorted
   121  	if !cfg.NoSort {
   122  		sort.Slice(publicKeys, func(i, j int) bool {
   123  			return publicKeys[i].Address().Compare(publicKeys[j].Address()) < 0
   124  		})
   125  	}
   126  
   127  	// Create a new public key with the multisig threshold
   128  	if _, err := kb.CreateMulti(
   129  		name,
   130  		multisig.NewPubKeyMultisigThreshold(cfg.MultisigThreshold, publicKeys),
   131  	); err != nil {
   132  		return fmt.Errorf("unable to create multisig key reference, %w", err)
   133  	}
   134  
   135  	io.Printfln("Key %q saved to disk.\n", name)
   136  
   137  	return nil
   138  }