github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/state/options.go (about) 1 // Copyright 2016, 2024 The TrueBlocks Authors. All rights reserved. 2 // Use of this source code is governed by a license that can 3 // be found in the LICENSE file. 4 /* 5 * Parts of this file were auto generated. Edit only those parts of 6 * the code inside of 'EXISTING_CODE' tags. 7 */ 8 9 package statePkg 10 11 import ( 12 // EXISTING_CODE 13 "encoding/json" 14 "io" 15 "net/http" 16 "net/url" 17 "strings" 18 19 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/internal/globals" 20 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 21 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/caps" 22 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers" 23 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 24 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 25 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate" 26 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk" 27 // EXISTING_CODE 28 ) 29 30 // StateOptions provides all command options for the chifra state command. 31 type StateOptions struct { 32 Addrs []string `json:"addrs,omitempty"` // One or more addresses (0x...) from which to retrieve balances 33 Blocks []string `json:"blocks,omitempty"` // An optional list of one or more blocks at which to report balances, defaults to 'latest' 34 BlockIds []identifiers.Identifier `json:"blockIds,omitempty"` // Block identifiers 35 Parts []string `json:"parts,omitempty"` // Control which state to export 36 Changes bool `json:"changes,omitempty"` // Only report a balance when it changes from one block to the next 37 NoZero bool `json:"noZero,omitempty"` // Suppress the display of zero balance accounts 38 Call string `json:"call,omitempty"` // Call a smart contract with one or more solidity calls, four-byte plus parameters, or encoded call data strings 39 Articulate bool `json:"articulate,omitempty"` // For the --call option only, articulate the retrieved data if ABIs can be found 40 ProxyFor string `json:"proxyFor,omitempty"` // For the --call option only, redirects calls to this implementation 41 Globals globals.GlobalOptions `json:"globals,omitempty"` // The global options 42 Conn *rpc.Connection `json:"conn,omitempty"` // The connection to the RPC server 43 BadFlag error `json:"badFlag,omitempty"` // An error flag if needed 44 // EXISTING_CODE 45 Calls []string `json:"-"` 46 // EXISTING_CODE 47 } 48 49 var defaultStateOptions = StateOptions{} 50 51 // testLog is used only during testing to export the options for this test case. 52 func (opts *StateOptions) testLog() { 53 logger.TestLog(len(opts.Addrs) > 0, "Addrs: ", opts.Addrs) 54 logger.TestLog(len(opts.Blocks) > 0, "Blocks: ", opts.Blocks) 55 logger.TestLog(len(opts.Parts) > 0, "Parts: ", opts.Parts) 56 logger.TestLog(opts.Changes, "Changes: ", opts.Changes) 57 logger.TestLog(opts.NoZero, "NoZero: ", opts.NoZero) 58 logger.TestLog(len(opts.Call) > 0, "Call: ", opts.Call) 59 logger.TestLog(opts.Articulate, "Articulate: ", opts.Articulate) 60 logger.TestLog(len(opts.ProxyFor) > 0, "ProxyFor: ", opts.ProxyFor) 61 opts.Conn.TestLog(opts.getCaches()) 62 opts.Globals.TestLog() 63 } 64 65 // String implements the Stringer interface 66 func (opts *StateOptions) String() string { 67 b, _ := json.MarshalIndent(opts, "", " ") 68 return string(b) 69 } 70 71 // stateFinishParseApi finishes the parsing for server invocations. Returns a new StateOptions. 72 func stateFinishParseApi(w http.ResponseWriter, r *http.Request) *StateOptions { 73 values := r.URL.Query() 74 if r.Header.Get("User-Agent") == "testRunner" { 75 values.Set("testRunner", "true") 76 } 77 return StateFinishParseInternal(w, values) 78 } 79 80 func StateFinishParseInternal(w io.Writer, values url.Values) *StateOptions { 81 copy := defaultStateOptions 82 copy.Globals.Caps = getCaps() 83 opts := © 84 for key, value := range values { 85 switch key { 86 case "addrs": 87 for _, val := range value { 88 s := strings.Split(val, " ") // may contain space separated items 89 opts.Addrs = append(opts.Addrs, s...) 90 } 91 case "blocks": 92 for _, val := range value { 93 s := strings.Split(val, " ") // may contain space separated items 94 opts.Blocks = append(opts.Blocks, s...) 95 } 96 case "parts": 97 for _, val := range value { 98 s := strings.Split(val, " ") // may contain space separated items 99 opts.Parts = append(opts.Parts, s...) 100 } 101 case "changes": 102 opts.Changes = true 103 case "noZero": 104 opts.NoZero = true 105 case "call": 106 opts.Call = value[0] 107 case "articulate": 108 opts.Articulate = true 109 case "proxyFor": 110 opts.ProxyFor = value[0] 111 default: 112 if !copy.Globals.Caps.HasKey(key) { 113 err := validate.Usage("Invalid key ({0}) in {1} route.", key, "state") 114 if opts.BadFlag == nil || opts.BadFlag.Error() > err.Error() { 115 opts.BadFlag = err 116 } 117 } 118 } 119 } 120 opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches()) 121 122 // EXISTING_CODE 123 opts.Call = strings.Replace(strings.Trim(opts.Call, "'"), "'", "\"", -1) 124 opts.Calls = strings.Split(opts.Call, ":") 125 if len(opts.Blocks) == 0 { 126 if opts.Globals.TestMode { 127 opts.Blocks = []string{"17000000"} 128 } else { 129 opts.Blocks = []string{"latest"} 130 } 131 } 132 // EXISTING_CODE 133 opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs) 134 opts.ProxyFor, _ = opts.Conn.GetEnsAddress(opts.ProxyFor) 135 136 return opts 137 } 138 139 // stateFinishParse finishes the parsing for command line invocations. Returns a new StateOptions. 140 func stateFinishParse(args []string) *StateOptions { 141 // remove duplicates from args if any (not needed in api mode because the server does it). 142 dedup := map[string]int{} 143 if len(args) > 0 { 144 tmp := []string{} 145 for _, arg := range args { 146 if value := dedup[arg]; value == 0 { 147 tmp = append(tmp, arg) 148 } 149 dedup[arg]++ 150 } 151 args = tmp 152 } 153 154 defFmt := "txt" 155 opts := GetOptions() 156 opts.Conn = opts.Globals.FinishParse(args, opts.getCaches()) 157 158 // EXISTING_CODE 159 for _, arg := range args { 160 if base.IsValidAddress(arg) { 161 opts.Addrs = append(opts.Addrs, arg) 162 } else { 163 opts.Blocks = append(opts.Blocks, arg) 164 } 165 } 166 opts.Call = strings.Replace(strings.Trim(opts.Call, "'"), "'", "\"", -1) 167 opts.Calls = strings.Split(opts.Call, ":") 168 if len(opts.Blocks) == 0 { 169 if opts.Globals.TestMode { 170 opts.Blocks = []string{"17000000"} 171 } else { 172 opts.Blocks = []string{"latest"} 173 } 174 } 175 // EXISTING_CODE 176 opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs) 177 opts.ProxyFor, _ = opts.Conn.GetEnsAddress(opts.ProxyFor) 178 if len(opts.Globals.Format) == 0 || opts.Globals.Format == "none" { 179 opts.Globals.Format = defFmt 180 } 181 182 return opts 183 } 184 185 func GetOptions() *StateOptions { 186 // EXISTING_CODE 187 // EXISTING_CODE 188 return &defaultStateOptions 189 } 190 191 func getCaps() caps.Capability { 192 var capabilities caps.Capability // capabilities for chifra state 193 capabilities = capabilities.Add(caps.Default) 194 capabilities = capabilities.Add(caps.Caching) 195 capabilities = capabilities.Add(caps.Ether) 196 capabilities = capabilities.Add(caps.Names) 197 // EXISTING_CODE 198 // EXISTING_CODE 199 return capabilities 200 } 201 202 func ResetOptions(testMode bool) { 203 // We want to keep writer between command file calls 204 w := GetOptions().Globals.Writer 205 opts := StateOptions{} 206 globals.SetDefaults(&opts.Globals) 207 opts.Globals.TestMode = testMode 208 opts.Globals.Writer = w 209 opts.Globals.Caps = getCaps() 210 defaultStateOptions = opts 211 } 212 213 func (opts *StateOptions) getCaches() (caches map[walk.CacheType]bool) { 214 // EXISTING_CODE 215 caches = map[walk.CacheType]bool{ 216 walk.Cache_State: true, 217 walk.Cache_Results: len(opts.Call) > 0, 218 } 219 // EXISTING_CODE 220 return 221 } 222 223 // EXISTING_CODE 224 // EXISTING_CODE