-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Expand file tree
/
Copy pathmsg.go
More file actions
164 lines (132 loc) · 5.25 KB
/
Copy pathmsg.go
File metadata and controls
164 lines (132 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package autocli
import (
"context"
"fmt"
"github.com/cockroachdb/errors"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/dynamicpb"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
"cosmossdk.io/client/v2/autocli/flag"
"cosmossdk.io/client/v2/internal/util"
"github.com/cosmos/cosmos-sdk/client"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
)
// BuildMsgCommand builds the msg commands for all the provided modules. If a custom command is provided for a
// module, this is used instead of any automatically generated CLI commands. This allows apps to a fully dynamic client
// with a more customized experience if a binary with custom commands is downloaded.
func (b *Builder) BuildMsgCommand(ctx context.Context, appOptions AppOptions, customCmds map[string]*cobra.Command) (*cobra.Command, error) {
msgCmd := topLevelCmd(ctx, "tx", "Transaction subcommands")
if err := b.enhanceCommandCommon(msgCmd, msgCmdType, appOptions, customCmds); err != nil {
return nil, err
}
return msgCmd, nil
}
// AddMsgServiceCommands adds a sub-command to the provided command for each
// method in the specified service and returns the command. This can be used in
// order to add auto-generated commands to an existing command.
func (b *Builder) AddMsgServiceCommands(cmd *cobra.Command, cmdDescriptor *autocliv1.ServiceCommandDescriptor) error {
for cmdName, subCmdDescriptor := range cmdDescriptor.SubCommands {
subCmd := findSubCommand(cmd, cmdName)
if subCmd == nil {
subCmd = topLevelCmd(cmd.Context(), cmdName, fmt.Sprintf("Tx commands for the %s service", subCmdDescriptor.Service))
}
// Add recursive sub-commands if there are any. This is used for nested services.
if err := b.AddMsgServiceCommands(subCmd, subCmdDescriptor); err != nil {
return err
}
cmd.AddCommand(subCmd)
}
if cmdDescriptor.Service == "" {
// skip empty command descriptor
return nil
}
descriptor, err := b.FileResolver.FindDescriptorByName(protoreflect.FullName(cmdDescriptor.Service))
if err != nil {
return errors.Errorf("can't find service %s: %v", cmdDescriptor.Service, err)
}
service := descriptor.(protoreflect.ServiceDescriptor)
methods := service.Methods()
rpcOptMap := map[protoreflect.Name]*autocliv1.RpcCommandOptions{}
for _, option := range cmdDescriptor.RpcCommandOptions {
methodName := protoreflect.Name(option.RpcMethod)
// validate that methods exist
if m := methods.ByName(methodName); m == nil {
return fmt.Errorf("rpc method %q not found for service %q", methodName, service.FullName())
}
rpcOptMap[methodName] = option
}
for i := 0; i < methods.Len(); i++ {
methodDescriptor := methods.Get(i)
methodOpts, ok := rpcOptMap[methodDescriptor.Name()]
if !ok {
methodOpts = &autocliv1.RpcCommandOptions{}
}
if methodOpts.Skip {
continue
}
if !util.IsSupportedVersion(methodDescriptor) {
continue
}
methodCmd, err := b.BuildMsgMethodCommand(methodDescriptor, methodOpts)
if err != nil {
return err
}
if findSubCommand(cmd, methodCmd.Name()) != nil {
// do not overwrite existing commands
// we do not display a warning because you may want to overwrite an autocli command
continue
}
if methodCmd != nil {
cmd.AddCommand(methodCmd)
}
}
return nil
}
// BuildMsgMethodCommand returns a command that outputs the JSON representation of the message.
func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor, options *autocliv1.RpcCommandOptions) (*cobra.Command, error) {
cmd, err := b.buildMethodCommandCommon(descriptor, options, func(cmd *cobra.Command, input protoreflect.Message) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
clientCtx = clientCtx.WithCmdContext(cmd.Context())
clientCtx = clientCtx.WithOutput(cmd.OutOrStdout())
// set signer to signer field if empty
fd := input.Descriptor().Fields().ByName(protoreflect.Name(flag.GetSignerFieldName(input.Descriptor())))
if addr := input.Get(fd).String(); addr == "" {
addressCodec := b.Builder.AddressCodec
scalarType, ok := flag.GetScalarType(fd)
if ok {
// override address codec if validator or consensus address
switch scalarType {
case flag.ValidatorAddressStringScalarType:
addressCodec = b.Builder.ValidatorAddressCodec
case flag.ConsensusAddressStringScalarType:
addressCodec = b.Builder.ConsensusAddressCodec
}
}
signerFromFlag := clientCtx.GetFromAddress()
signer, err := addressCodec.BytesToString(signerFromFlag.Bytes())
if err != nil {
return fmt.Errorf("failed to set signer on message, got %v: %w", signerFromFlag, err)
}
input.Set(fd, protoreflect.ValueOfString(signer))
}
// AutoCLI uses protov2 messages, while the SDK only supports proto v1 messages.
// Here we use dynamicpb, to create a proto v1 compatible message.
// The SDK codec will handle protov2 -> protov1 (marshal)
msg := dynamicpb.NewMessage(input.Descriptor())
proto.Merge(msg, input.Interface())
return clienttx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
})
if b.AddTxConnFlags != nil {
b.AddTxConnFlags(cmd)
}
// silence usage only for inner txs & queries commands
if cmd != nil {
cmd.SilenceUsage = true
}
return cmd, err
}