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 }