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