github.com/cosmos/cosmos-sdk@v0.50.10/client/context.go (about) 1 package client 2 3 import ( 4 "bufio" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "os" 10 11 "github.com/cosmos/gogoproto/proto" 12 "github.com/spf13/viper" 13 "google.golang.org/grpc" 14 "sigs.k8s.io/yaml" 15 16 "github.com/cosmos/cosmos-sdk/codec" 17 codectypes "github.com/cosmos/cosmos-sdk/codec/types" 18 "github.com/cosmos/cosmos-sdk/crypto/keyring" 19 sdk "github.com/cosmos/cosmos-sdk/types" 20 ) 21 22 // PreprocessTxFn defines a hook by which chains can preprocess transactions before broadcasting 23 type PreprocessTxFn func(chainID string, key keyring.KeyType, tx TxBuilder) error 24 25 // Context implements a typical context created in SDK modules for transaction 26 // handling and queries. 27 type Context struct { 28 FromAddress sdk.AccAddress 29 Client CometRPC 30 GRPCClient *grpc.ClientConn 31 ChainID string 32 Codec codec.Codec 33 InterfaceRegistry codectypes.InterfaceRegistry 34 Input io.Reader 35 Keyring keyring.Keyring 36 KeyringOptions []keyring.Option 37 Output io.Writer 38 OutputFormat string 39 Height int64 40 HomeDir string 41 KeyringDir string 42 From string 43 BroadcastMode string 44 FromName string 45 SignModeStr string 46 UseLedger bool 47 Simulate bool 48 GenerateOnly bool 49 Offline bool 50 SkipConfirm bool 51 TxConfig TxConfig 52 AccountRetriever AccountRetriever 53 NodeURI string 54 FeePayer sdk.AccAddress 55 FeeGranter sdk.AccAddress 56 Viper *viper.Viper 57 LedgerHasProtobuf bool 58 PreprocessTxHook PreprocessTxFn 59 60 // IsAux is true when the signer is an auxiliary signer (e.g. the tipper). 61 IsAux bool 62 63 // TODO: Deprecated (remove). 64 LegacyAmino *codec.LegacyAmino 65 66 // CmdContext is the context.Context from the Cobra command. 67 CmdContext context.Context 68 } 69 70 // WithCmdContext returns a copy of the context with an updated context.Context, 71 // usually set to the cobra cmd context. 72 func (ctx Context) WithCmdContext(c context.Context) Context { 73 ctx.CmdContext = c 74 return ctx 75 } 76 77 // WithKeyring returns a copy of the context with an updated keyring. 78 func (ctx Context) WithKeyring(k keyring.Keyring) Context { 79 ctx.Keyring = k 80 return ctx 81 } 82 83 // WithKeyringOptions returns a copy of the context with an updated keyring. 84 func (ctx Context) WithKeyringOptions(opts ...keyring.Option) Context { 85 ctx.KeyringOptions = opts 86 return ctx 87 } 88 89 // WithInput returns a copy of the context with an updated input. 90 func (ctx Context) WithInput(r io.Reader) Context { 91 // convert to a bufio.Reader to have a shared buffer between the keyring and the 92 // the Commands, ensuring a read from one advance the read pointer for the other. 93 // see https://github.com/cosmos/cosmos-sdk/issues/9566. 94 ctx.Input = bufio.NewReader(r) 95 return ctx 96 } 97 98 // WithCodec returns a copy of the Context with an updated Codec. 99 func (ctx Context) WithCodec(m codec.Codec) Context { 100 ctx.Codec = m 101 return ctx 102 } 103 104 // WithLegacyAmino returns a copy of the context with an updated LegacyAmino codec. 105 // TODO: Deprecated (remove). 106 func (ctx Context) WithLegacyAmino(cdc *codec.LegacyAmino) Context { 107 ctx.LegacyAmino = cdc 108 return ctx 109 } 110 111 // WithOutput returns a copy of the context with an updated output writer (e.g. stdout). 112 func (ctx Context) WithOutput(w io.Writer) Context { 113 ctx.Output = w 114 return ctx 115 } 116 117 // WithFrom returns a copy of the context with an updated from address or name. 118 func (ctx Context) WithFrom(from string) Context { 119 ctx.From = from 120 return ctx 121 } 122 123 // WithOutputFormat returns a copy of the context with an updated OutputFormat field. 124 func (ctx Context) WithOutputFormat(format string) Context { 125 ctx.OutputFormat = format 126 return ctx 127 } 128 129 // WithNodeURI returns a copy of the context with an updated node URI. 130 func (ctx Context) WithNodeURI(nodeURI string) Context { 131 ctx.NodeURI = nodeURI 132 return ctx 133 } 134 135 // WithHeight returns a copy of the context with an updated height. 136 func (ctx Context) WithHeight(height int64) Context { 137 ctx.Height = height 138 return ctx 139 } 140 141 // WithClient returns a copy of the context with an updated RPC client 142 // instance. 143 func (ctx Context) WithClient(client CometRPC) Context { 144 ctx.Client = client 145 return ctx 146 } 147 148 // WithGRPCClient returns a copy of the context with an updated GRPC client 149 // instance. 150 func (ctx Context) WithGRPCClient(grpcClient *grpc.ClientConn) Context { 151 ctx.GRPCClient = grpcClient 152 return ctx 153 } 154 155 // WithUseLedger returns a copy of the context with an updated UseLedger flag. 156 func (ctx Context) WithUseLedger(useLedger bool) Context { 157 ctx.UseLedger = useLedger 158 return ctx 159 } 160 161 // WithChainID returns a copy of the context with an updated chain ID. 162 func (ctx Context) WithChainID(chainID string) Context { 163 ctx.ChainID = chainID 164 return ctx 165 } 166 167 // WithHomeDir returns a copy of the Context with HomeDir set. 168 func (ctx Context) WithHomeDir(dir string) Context { 169 if dir != "" { 170 ctx.HomeDir = dir 171 } 172 return ctx 173 } 174 175 // WithKeyringDir returns a copy of the Context with KeyringDir set. 176 func (ctx Context) WithKeyringDir(dir string) Context { 177 ctx.KeyringDir = dir 178 return ctx 179 } 180 181 // WithGenerateOnly returns a copy of the context with updated GenerateOnly value 182 func (ctx Context) WithGenerateOnly(generateOnly bool) Context { 183 ctx.GenerateOnly = generateOnly 184 return ctx 185 } 186 187 // WithSimulation returns a copy of the context with updated Simulate value 188 func (ctx Context) WithSimulation(simulate bool) Context { 189 ctx.Simulate = simulate 190 return ctx 191 } 192 193 // WithOffline returns a copy of the context with updated Offline value. 194 func (ctx Context) WithOffline(offline bool) Context { 195 ctx.Offline = offline 196 return ctx 197 } 198 199 // WithFromName returns a copy of the context with an updated from account name. 200 func (ctx Context) WithFromName(name string) Context { 201 ctx.FromName = name 202 return ctx 203 } 204 205 // WithFromAddress returns a copy of the context with an updated from account 206 // address. 207 func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context { 208 ctx.FromAddress = addr 209 return ctx 210 } 211 212 // WithFeePayerAddress returns a copy of the context with an updated fee payer account 213 // address. 214 func (ctx Context) WithFeePayerAddress(addr sdk.AccAddress) Context { 215 ctx.FeePayer = addr 216 return ctx 217 } 218 219 // WithFeeGranterAddress returns a copy of the context with an updated fee granter account 220 // address. 221 func (ctx Context) WithFeeGranterAddress(addr sdk.AccAddress) Context { 222 ctx.FeeGranter = addr 223 return ctx 224 } 225 226 // WithBroadcastMode returns a copy of the context with an updated broadcast 227 // mode. 228 func (ctx Context) WithBroadcastMode(mode string) Context { 229 ctx.BroadcastMode = mode 230 return ctx 231 } 232 233 // WithSignModeStr returns a copy of the context with an updated SignMode 234 // value. 235 func (ctx Context) WithSignModeStr(signModeStr string) Context { 236 ctx.SignModeStr = signModeStr 237 return ctx 238 } 239 240 // WithSkipConfirmation returns a copy of the context with an updated SkipConfirm 241 // value. 242 func (ctx Context) WithSkipConfirmation(skip bool) Context { 243 ctx.SkipConfirm = skip 244 return ctx 245 } 246 247 // WithTxConfig returns the context with an updated TxConfig 248 func (ctx Context) WithTxConfig(generator TxConfig) Context { 249 ctx.TxConfig = generator 250 return ctx 251 } 252 253 // WithAccountRetriever returns the context with an updated AccountRetriever 254 func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context { 255 ctx.AccountRetriever = retriever 256 return ctx 257 } 258 259 // WithInterfaceRegistry returns the context with an updated InterfaceRegistry 260 func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context { 261 ctx.InterfaceRegistry = interfaceRegistry 262 return ctx 263 } 264 265 // WithViper returns the context with Viper field. This Viper instance is used to read 266 // client-side config from the config file. 267 func (ctx Context) WithViper(prefix string) Context { 268 v := viper.New() 269 v.SetEnvPrefix(prefix) 270 v.AutomaticEnv() 271 ctx.Viper = v 272 return ctx 273 } 274 275 // WithAux returns a copy of the context with an updated IsAux value. 276 func (ctx Context) WithAux(isAux bool) Context { 277 ctx.IsAux = isAux 278 return ctx 279 } 280 281 // WithLedgerHasProto returns the context with the provided boolean value, indicating 282 // whether the target Ledger application can support Protobuf payloads. 283 func (ctx Context) WithLedgerHasProtobuf(val bool) Context { 284 ctx.LedgerHasProtobuf = val 285 return ctx 286 } 287 288 // WithPreprocessTxHook returns the context with the provided preprocessing hook, which 289 // enables chains to preprocess the transaction using the builder. 290 func (ctx Context) WithPreprocessTxHook(preprocessFn PreprocessTxFn) Context { 291 ctx.PreprocessTxHook = preprocessFn 292 return ctx 293 } 294 295 // PrintString prints the raw string to ctx.Output if it's defined, otherwise to os.Stdout 296 func (ctx Context) PrintString(str string) error { 297 return ctx.PrintBytes([]byte(str)) 298 } 299 300 // PrintBytes prints the raw bytes to ctx.Output if it's defined, otherwise to os.Stdout. 301 // NOTE: for printing a complex state object, you should use ctx.PrintOutput 302 func (ctx Context) PrintBytes(o []byte) error { 303 writer := ctx.Output 304 if writer == nil { 305 writer = os.Stdout 306 } 307 308 _, err := writer.Write(o) 309 return err 310 } 311 312 // PrintProto outputs toPrint to the ctx.Output based on ctx.OutputFormat which is 313 // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint 314 // will be JSON encoded using ctx.Codec. An error is returned upon failure. 315 func (ctx Context) PrintProto(toPrint proto.Message) error { 316 // always serialize JSON initially because proto json can't be directly YAML encoded 317 out, err := ctx.Codec.MarshalJSON(toPrint) 318 if err != nil { 319 return err 320 } 321 return ctx.printOutput(out) 322 } 323 324 // PrintObjectLegacy is a variant of PrintProto that doesn't require a proto.Message type 325 // and uses amino JSON encoding. 326 // Deprecated: It will be removed in the near future! 327 func (ctx Context) PrintObjectLegacy(toPrint interface{}) error { 328 out, err := ctx.LegacyAmino.MarshalJSON(toPrint) 329 if err != nil { 330 return err 331 } 332 return ctx.printOutput(out) 333 } 334 335 // PrintRaw is a variant of PrintProto that doesn't require a proto.Message type 336 // and uses a raw JSON message. No marshaling is performed. 337 func (ctx Context) PrintRaw(toPrint json.RawMessage) error { 338 return ctx.printOutput(toPrint) 339 } 340 341 func (ctx Context) printOutput(out []byte) error { 342 var err error 343 if ctx.OutputFormat == "text" { 344 out, err = yaml.JSONToYAML(out) 345 if err != nil { 346 return err 347 } 348 } 349 350 writer := ctx.Output 351 if writer == nil { 352 writer = os.Stdout 353 } 354 355 _, err = writer.Write(out) 356 if err != nil { 357 return err 358 } 359 360 if ctx.OutputFormat != "text" { 361 // append new-line for formats besides YAML 362 _, err = writer.Write([]byte("\n")) 363 if err != nil { 364 return err 365 } 366 } 367 368 return nil 369 } 370 371 // GetFromFields returns a from account address, account name and keyring type, given either an address or key name. 372 // If clientCtx.Simulate is true the keystore is not accessed and a valid address must be provided 373 // If clientCtx.GenerateOnly is true the keystore is only accessed if a key name is provided 374 func GetFromFields(clientCtx Context, kr keyring.Keyring, from string) (sdk.AccAddress, string, keyring.KeyType, error) { 375 if from == "" { 376 return nil, "", 0, nil 377 } 378 379 addr, err := sdk.AccAddressFromBech32(from) 380 switch { 381 case clientCtx.Simulate: 382 if err != nil { 383 return nil, "", 0, fmt.Errorf("a valid bech32 address must be provided in simulation mode: %w", err) 384 } 385 386 return addr, "", 0, nil 387 388 case clientCtx.GenerateOnly: 389 if err == nil { 390 return addr, "", 0, nil 391 } 392 } 393 394 var k *keyring.Record 395 if err == nil { 396 k, err = kr.KeyByAddress(addr) 397 if err != nil { 398 return nil, "", 0, err 399 } 400 } else { 401 k, err = kr.Key(from) 402 if err != nil { 403 return nil, "", 0, err 404 } 405 } 406 407 addr, err = k.GetAddress() 408 if err != nil { 409 return nil, "", 0, err 410 } 411 412 return addr, k.Name, k.GetType(), nil 413 } 414 415 // NewKeyringFromBackend gets a Keyring object from a backend 416 func NewKeyringFromBackend(ctx Context, backend string) (keyring.Keyring, error) { 417 if ctx.Simulate { 418 backend = keyring.BackendMemory 419 } 420 421 return keyring.New(sdk.KeyringServiceName(), backend, ctx.KeyringDir, ctx.Input, ctx.Codec, ctx.KeyringOptions...) 422 }