Skip to content

Commit af4b6ed

Browse files
author
Alessio Treglia
committed
Add multisign command
Ref: #3198
1 parent 1cc0984 commit af4b6ed

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

cmd/gaia/cmd/gaiacli/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command {
136136
bankcmd.SendTxCmd(cdc),
137137
client.LineBreak,
138138
authcmd.GetSignCommand(cdc),
139+
authcmd.GetMultiSignCommand(cdc),
139140
bankcmd.GetBroadcastCommand(cdc),
140141
client.LineBreak,
141142
)

x/auth/client/cli/multisign.go

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
8+
"github.com/spf13/cobra"
9+
"github.com/spf13/viper"
10+
amino "github.com/tendermint/go-amino"
11+
"github.com/tendermint/tendermint/crypto/multisig"
12+
"github.com/tendermint/tendermint/libs/cli"
13+
14+
"github.com/cosmos/cosmos-sdk/client"
15+
"github.com/cosmos/cosmos-sdk/client/context"
16+
"github.com/cosmos/cosmos-sdk/client/keys"
17+
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
18+
"github.com/cosmos/cosmos-sdk/x/auth"
19+
)
20+
21+
// GetSignCommand returns the sign command
22+
func GetMultiSignCommand(codec *amino.Codec) *cobra.Command {
23+
cmd := &cobra.Command{
24+
Use: "multisign <file> <name> <<signature>...>",
25+
Short: "Generate multisig signatures for transactions generated offline",
26+
Long: `Sign transactions created with the --generate-only flag that require multisig signatures.
27+
28+
Read signature(s) from <signature> file(s), generate a multisig signature compliant to the
29+
offline multisig key <name>, and attach it to the transaction read from <file>.
30+
31+
The --offline flag makes sure that the client will not reach out to an external node.
32+
Thus account number or sequence number lookups will not be performed and it is
33+
recommended to set such parameters manually.`,
34+
RunE: makeMultiSignCmd(codec),
35+
Args: cobra.MinimumNArgs(3),
36+
}
37+
cmd.Flags().String(client.FlagName, "", "Name of multisig (offline) public key")
38+
cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit.")
39+
cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query a full node.")
40+
cmd.Flags().String(flagOutfile, "",
41+
"The document will be written to the given file instead of STDOUT")
42+
43+
// Add the flags here and return the command
44+
return client.PostCommands(cmd)[0]
45+
}
46+
47+
func makeMultiSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error {
48+
return func(cmd *cobra.Command, args []string) (err error) {
49+
stdTx, err := readAndUnmarshalStdTx(cdc, args[0])
50+
if err != nil {
51+
return
52+
}
53+
54+
keybase, err := keys.GetKeyBaseFromDir(viper.GetString(cli.HomeFlag))
55+
if err != nil {
56+
return
57+
}
58+
59+
multisigInfo, err := keybase.Get(args[1])
60+
if err != nil {
61+
return
62+
}
63+
if multisigInfo.GetType() != crkeys.TypeOffline {
64+
return fmt.Errorf("%q must be of type offline: %s",
65+
args[1], multisigInfo.GetType())
66+
}
67+
68+
multisigPub := multisigInfo.GetPubKey().(*multisig.PubKeyMultisigThreshold)
69+
multisigSig := multisig.NewMultisig(len(args) - 2)
70+
for i := 2; i < len(args); i++ {
71+
stdSig, err := readAndUnmarshalStdSignature(cdc, args[i])
72+
if err != nil {
73+
return err
74+
}
75+
multisigSig.AddSignatureFromPubKey(stdSig.Signature, stdSig.PubKey, multisigPub.PubKeys)
76+
}
77+
78+
cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(cdc)
79+
newStdSig := auth.StdSignature{Signature: multisigSig.Marshal(), PubKey: multisigPub}
80+
newTx := auth.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []auth.StdSignature{newStdSig}, stdTx.GetMemo())
81+
82+
var json []byte
83+
switch viper.GetBool(flagSigOnly) {
84+
case true:
85+
switch cliCtx.Indent {
86+
case true:
87+
json, err = cdc.MarshalJSONIndent(newTx.Signatures[0], "", " ")
88+
default:
89+
json, err = cdc.MarshalJSON(newTx.Signatures[0])
90+
}
91+
default:
92+
switch cliCtx.Indent {
93+
case true:
94+
json, err = cdc.MarshalJSONIndent(newTx, "", " ")
95+
default:
96+
json, err = cdc.MarshalJSON(newTx)
97+
}
98+
}
99+
if err != nil {
100+
return err
101+
}
102+
103+
if viper.GetString(flagOutfile) == "" {
104+
fmt.Printf("%s\n", json)
105+
return
106+
}
107+
108+
fp, err := os.OpenFile(
109+
viper.GetString(flagOutfile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644,
110+
)
111+
if err != nil {
112+
return err
113+
}
114+
defer fp.Close()
115+
116+
fmt.Fprintf(fp, "%s\n", json)
117+
118+
return
119+
}
120+
}
121+
122+
func readAndUnmarshalStdSignature(cdc *amino.Codec, filename string) (stdSig auth.StdSignature, err error) {
123+
var bytes []byte
124+
if bytes, err = ioutil.ReadFile(filename); err != nil {
125+
return
126+
}
127+
if err = cdc.UnmarshalJSON(bytes, &stdSig); err != nil {
128+
return
129+
}
130+
return
131+
}

0 commit comments

Comments
 (0)