github.com/Finschia/finschia-sdk@v0.48.1/client/context.go (about) 1 package client 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "io" 7 "os" 8 9 "github.com/spf13/viper" 10 11 "gopkg.in/yaml.v2" 12 13 rpcclient "github.com/Finschia/ostracon/rpc/client" 14 "github.com/gogo/protobuf/proto" 15 "github.com/pkg/errors" 16 17 "github.com/Finschia/finschia-sdk/codec" 18 codectypes "github.com/Finschia/finschia-sdk/codec/types" 19 "github.com/Finschia/finschia-sdk/crypto/keyring" 20 sdk "github.com/Finschia/finschia-sdk/types" 21 ) 22 23 // Context implements a typical context created in SDK modules for transaction 24 // handling and queries. 25 type Context struct { 26 FromAddress sdk.AccAddress 27 Client rpcclient.Client 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 // WithUseLedger returns a copy of the context with an updated UseLedger flag. 151 func (ctx Context) WithUseLedger(useLedger bool) Context { 152 ctx.UseLedger = useLedger 153 return ctx 154 } 155 156 // WithChainID returns a copy of the context with an updated chain ID. 157 func (ctx Context) WithChainID(chainID string) Context { 158 ctx.ChainID = chainID 159 return ctx 160 } 161 162 // WithHomeDir returns a copy of the Context with HomeDir set. 163 func (ctx Context) WithHomeDir(dir string) Context { 164 if dir != "" { 165 ctx.HomeDir = dir 166 } 167 return ctx 168 } 169 170 // WithKeyringDir returns a copy of the Context with KeyringDir set. 171 func (ctx Context) WithKeyringDir(dir string) Context { 172 ctx.KeyringDir = dir 173 return ctx 174 } 175 176 // WithGenerateOnly returns a copy of the context with updated GenerateOnly value 177 func (ctx Context) WithGenerateOnly(generateOnly bool) Context { 178 ctx.GenerateOnly = generateOnly 179 return ctx 180 } 181 182 // WithSimulation returns a copy of the context with updated Simulate value 183 func (ctx Context) WithSimulation(simulate bool) Context { 184 ctx.Simulate = simulate 185 return ctx 186 } 187 188 // WithOffline returns a copy of the context with updated Offline value. 189 func (ctx Context) WithOffline(offline bool) Context { 190 ctx.Offline = offline 191 return ctx 192 } 193 194 // WithFromName returns a copy of the context with an updated from account name. 195 func (ctx Context) WithFromName(name string) Context { 196 ctx.FromName = name 197 return ctx 198 } 199 200 // WithFromAddress returns a copy of the context with an updated from account 201 // address. 202 func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context { 203 ctx.FromAddress = addr 204 return ctx 205 } 206 207 // WithBroadcastMode returns a copy of the context with an updated broadcast 208 // mode. 209 func (ctx Context) WithBroadcastMode(mode string) Context { 210 ctx.BroadcastMode = mode 211 return ctx 212 } 213 214 // WithSignModeStr returns a copy of the context with an updated SignMode 215 // value. 216 func (ctx Context) WithSignModeStr(signModeStr string) Context { 217 ctx.SignModeStr = signModeStr 218 return ctx 219 } 220 221 // WithSkipConfirmation returns a copy of the context with an updated SkipConfirm 222 // value. 223 func (ctx Context) WithSkipConfirmation(skip bool) Context { 224 ctx.SkipConfirm = skip 225 return ctx 226 } 227 228 // WithTxConfig returns the context with an updated TxConfig 229 func (ctx Context) WithTxConfig(generator TxConfig) Context { 230 ctx.TxConfig = generator 231 return ctx 232 } 233 234 // WithAccountRetriever returns the context with an updated AccountRetriever 235 func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context { 236 ctx.AccountRetriever = retriever 237 return ctx 238 } 239 240 // WithInterfaceRegistry returns the context with an updated InterfaceRegistry 241 func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context { 242 ctx.InterfaceRegistry = interfaceRegistry 243 return ctx 244 } 245 246 // WithViper returns the context with Viper field. This Viper instance is used to read 247 // client-side config from the config file. 248 func (ctx Context) WithViper(prefix string) Context { 249 v := viper.New() 250 v.SetEnvPrefix(prefix) 251 v.AutomaticEnv() 252 ctx.Viper = v 253 return ctx 254 } 255 256 // PrintString prints the raw string to ctx.Output if it's defined, otherwise to os.Stdout 257 func (ctx Context) PrintString(str string) error { 258 return ctx.PrintBytes([]byte(str)) 259 } 260 261 // PrintBytes prints the raw bytes to ctx.Output if it's defined, otherwise to os.Stdout. 262 // NOTE: for printing a complex state object, you should use ctx.PrintOutput 263 func (ctx Context) PrintBytes(o []byte) error { 264 writer := ctx.Output 265 if writer == nil { 266 writer = os.Stdout 267 } 268 269 _, err := writer.Write(o) 270 return err 271 } 272 273 // PrintProto outputs toPrint to the ctx.Output based on ctx.OutputFormat which is 274 // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint 275 // will be JSON encoded using ctx.Codec. An error is returned upon failure. 276 func (ctx Context) PrintProto(toPrint proto.Message) error { 277 // always serialize JSON initially because proto json can't be directly YAML encoded 278 out, err := ctx.Codec.MarshalJSON(toPrint) 279 if err != nil { 280 return err 281 } 282 return ctx.printOutput(out) 283 } 284 285 // PrintObjectLegacy is a variant of PrintProto that doesn't require a proto.Message type 286 // and uses amino JSON encoding. 287 // Deprecated: It will be removed in the near future! 288 func (ctx Context) PrintObjectLegacy(toPrint interface{}) error { 289 out, err := ctx.LegacyAmino.MarshalJSON(toPrint) 290 if err != nil { 291 return err 292 } 293 return ctx.printOutput(out) 294 } 295 296 func (ctx Context) printOutput(out []byte) error { 297 if ctx.OutputFormat == "text" { 298 // handle text format by decoding and re-encoding JSON as YAML 299 var j interface{} 300 301 err := json.Unmarshal(out, &j) 302 if err != nil { 303 return err 304 } 305 306 out, err = yaml.Marshal(j) 307 if err != nil { 308 return err 309 } 310 } 311 312 writer := ctx.Output 313 if writer == nil { 314 writer = os.Stdout 315 } 316 317 _, err := writer.Write(out) 318 if err != nil { 319 return err 320 } 321 322 if ctx.OutputFormat != "text" { 323 // append new-line for formats besides YAML 324 _, err = writer.Write([]byte("\n")) 325 if err != nil { 326 return err 327 } 328 } 329 330 return nil 331 } 332 333 // GetFromFields returns a from account address, account name and keyring type, given either 334 // an address or key name. If genOnly is true, only a valid Bech32 cosmos 335 // address is returned. 336 func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddress, string, keyring.KeyType, error) { 337 if from == "" { 338 return nil, "", 0, nil 339 } 340 341 if genOnly { 342 addr, err := sdk.AccAddressFromBech32(from) 343 if err != nil { 344 return nil, "", 0, errors.Wrap(err, "must provide a valid Bech32 address in generate-only mode") 345 } 346 347 return addr, "", 0, nil 348 } 349 350 var info keyring.Info 351 if addr, err := sdk.AccAddressFromBech32(from); err == nil { 352 info, err = kr.KeyByAddress(addr) 353 if err != nil { 354 return nil, "", 0, err 355 } 356 } else { 357 info, err = kr.Key(from) 358 if err != nil { 359 return nil, "", 0, err 360 } 361 } 362 363 return info.GetAddress(), info.GetName(), info.GetType(), nil 364 } 365 366 // NewKeyringFromBackend gets a Keyring object from a backend 367 func NewKeyringFromBackend(ctx Context, backend string) (keyring.Keyring, error) { 368 if ctx.GenerateOnly || ctx.Simulate { 369 return keyring.New(sdk.KeyringServiceName(), keyring.BackendMemory, ctx.KeyringDir, ctx.Input) 370 } 371 372 return keyring.New(sdk.KeyringServiceName(), backend, ctx.KeyringDir, ctx.Input, ctx.KeyringOptions...) 373 }