github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/export/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 exportPkg 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/logger" 23 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 24 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate" 25 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk" 26 // EXISTING_CODE 27 ) 28 29 // ExportOptions provides all command options for the chifra export command. 30 type ExportOptions struct { 31 Addrs []string `json:"addrs,omitempty"` // One or more addresses (0x...) to export 32 Topics []string `json:"topics,omitempty"` // Filter by one or more log topics (only for --logs option) 33 Fourbytes []string `json:"fourbytes,omitempty"` // Filter by one or more fourbytes (only for transactions and trace options) 34 Appearances bool `json:"appearances,omitempty"` // Export a list of appearances 35 Receipts bool `json:"receipts,omitempty"` // Export receipts instead of transactional data 36 Logs bool `json:"logs,omitempty"` // Export logs instead of transactional data 37 Traces bool `json:"traces,omitempty"` // Export traces instead of transactional data 38 Neighbors bool `json:"neighbors,omitempty"` // Export the neighbors of the given address 39 Accounting bool `json:"accounting,omitempty"` // Attach accounting records to the exported data (applies to transactions export only) 40 Statements bool `json:"statements,omitempty"` // For the accounting options only, export only statements 41 Balances bool `json:"balances,omitempty"` // Traverse the transaction history and show each change in ETH balances 42 Withdrawals bool `json:"withdrawals,omitempty"` // Export withdrawals for the given address 43 Articulate bool `json:"articulate,omitempty"` // Articulate transactions, traces, logs, and outputs 44 CacheTraces bool `json:"cacheTraces,omitempty"` // Force the transaction's traces into the cache 45 Count bool `json:"count,omitempty"` // For --appearances mode only, display only the count of records 46 FirstRecord uint64 `json:"firstRecord,omitempty"` // The first record to process 47 MaxRecords uint64 `json:"maxRecords,omitempty"` // The maximum number of records to process 48 Relevant bool `json:"relevant,omitempty"` // For log and accounting export only, export only logs relevant to one of the given export addresses 49 Emitter []string `json:"emitter,omitempty"` // For the --logs option only, filter logs to show only those logs emitted by the given address(es) 50 Topic []string `json:"topic,omitempty"` // For the --logs option only, filter logs to show only those with this topic(s) 51 Reverted bool `json:"reverted,omitempty"` // Export only transactions that were reverted 52 Asset []string `json:"asset,omitempty"` // For the accounting options only, export statements only for this asset 53 Flow string `json:"flow,omitempty"` // For the accounting options only, export statements with incoming, outgoing, or zero value 54 Factory bool `json:"factory,omitempty"` // For --traces only, report addresses created by (or self-destructed by) the given address(es) 55 Unripe bool `json:"unripe,omitempty"` // Export transactions labeled unripe (i.e. less than 28 blocks old) 56 Reversed bool `json:"reversed,omitempty"` // Produce results in reverse chronological order 57 NoZero bool `json:"noZero,omitempty"` // For the --count option only, suppress the display of zero appearance accounts 58 FirstBlock base.Blknum `json:"firstBlock,omitempty"` // First block to process (inclusive) 59 LastBlock base.Blknum `json:"lastBlock,omitempty"` // Last block to process (inclusive) 60 Globals globals.GlobalOptions `json:"globals,omitempty"` // The global options 61 Conn *rpc.Connection `json:"conn,omitempty"` // The connection to the RPC server 62 BadFlag error `json:"badFlag,omitempty"` // An error flag if needed 63 // EXISTING_CODE 64 // EXISTING_CODE 65 } 66 67 var defaultExportOptions = ExportOptions{ 68 MaxRecords: 250, 69 LastBlock: base.NOPOSN, 70 } 71 72 // testLog is used only during testing to export the options for this test case. 73 func (opts *ExportOptions) testLog() { 74 logger.TestLog(len(opts.Addrs) > 0, "Addrs: ", opts.Addrs) 75 logger.TestLog(len(opts.Topics) > 0, "Topics: ", opts.Topics) 76 logger.TestLog(len(opts.Fourbytes) > 0, "Fourbytes: ", opts.Fourbytes) 77 logger.TestLog(opts.Appearances, "Appearances: ", opts.Appearances) 78 logger.TestLog(opts.Receipts, "Receipts: ", opts.Receipts) 79 logger.TestLog(opts.Logs, "Logs: ", opts.Logs) 80 logger.TestLog(opts.Traces, "Traces: ", opts.Traces) 81 logger.TestLog(opts.Neighbors, "Neighbors: ", opts.Neighbors) 82 logger.TestLog(opts.Accounting, "Accounting: ", opts.Accounting) 83 logger.TestLog(opts.Statements, "Statements: ", opts.Statements) 84 logger.TestLog(opts.Balances, "Balances: ", opts.Balances) 85 logger.TestLog(opts.Withdrawals, "Withdrawals: ", opts.Withdrawals) 86 logger.TestLog(opts.Articulate, "Articulate: ", opts.Articulate) 87 logger.TestLog(opts.CacheTraces, "CacheTraces: ", opts.CacheTraces) 88 logger.TestLog(opts.Count, "Count: ", opts.Count) 89 logger.TestLog(opts.FirstRecord != 0, "FirstRecord: ", opts.FirstRecord) 90 logger.TestLog(opts.MaxRecords != 250, "MaxRecords: ", opts.MaxRecords) 91 logger.TestLog(opts.Relevant, "Relevant: ", opts.Relevant) 92 logger.TestLog(len(opts.Emitter) > 0, "Emitter: ", opts.Emitter) 93 logger.TestLog(len(opts.Topic) > 0, "Topic: ", opts.Topic) 94 logger.TestLog(opts.Reverted, "Reverted: ", opts.Reverted) 95 logger.TestLog(len(opts.Asset) > 0, "Asset: ", opts.Asset) 96 logger.TestLog(len(opts.Flow) > 0, "Flow: ", opts.Flow) 97 logger.TestLog(opts.Factory, "Factory: ", opts.Factory) 98 logger.TestLog(opts.Unripe, "Unripe: ", opts.Unripe) 99 logger.TestLog(opts.Reversed, "Reversed: ", opts.Reversed) 100 logger.TestLog(opts.NoZero, "NoZero: ", opts.NoZero) 101 logger.TestLog(opts.FirstBlock != 0, "FirstBlock: ", opts.FirstBlock) 102 logger.TestLog(opts.LastBlock != base.NOPOSN && opts.LastBlock != 0, "LastBlock: ", opts.LastBlock) 103 opts.Conn.TestLog(opts.getCaches()) 104 opts.Globals.TestLog() 105 } 106 107 // String implements the Stringer interface 108 func (opts *ExportOptions) String() string { 109 b, _ := json.MarshalIndent(opts, "", " ") 110 return string(b) 111 } 112 113 // exportFinishParseApi finishes the parsing for server invocations. Returns a new ExportOptions. 114 func exportFinishParseApi(w http.ResponseWriter, r *http.Request) *ExportOptions { 115 values := r.URL.Query() 116 if r.Header.Get("User-Agent") == "testRunner" { 117 values.Set("testRunner", "true") 118 } 119 return ExportFinishParseInternal(w, values) 120 } 121 122 func ExportFinishParseInternal(w io.Writer, values url.Values) *ExportOptions { 123 copy := defaultExportOptions 124 copy.Globals.Caps = getCaps() 125 opts := © 126 opts.MaxRecords = 250 127 opts.LastBlock = base.NOPOSN 128 for key, value := range values { 129 switch key { 130 case "addrs": 131 for _, val := range value { 132 s := strings.Split(val, " ") // may contain space separated items 133 opts.Addrs = append(opts.Addrs, s...) 134 } 135 case "topics": 136 for _, val := range value { 137 s := strings.Split(val, " ") // may contain space separated items 138 opts.Topics = append(opts.Topics, s...) 139 } 140 case "fourbytes": 141 for _, val := range value { 142 s := strings.Split(val, " ") // may contain space separated items 143 opts.Fourbytes = append(opts.Fourbytes, s...) 144 } 145 case "appearances": 146 opts.Appearances = true 147 case "receipts": 148 opts.Receipts = true 149 case "logs": 150 opts.Logs = true 151 case "traces": 152 opts.Traces = true 153 case "neighbors": 154 opts.Neighbors = true 155 case "accounting": 156 opts.Accounting = true 157 case "statements": 158 opts.Statements = true 159 case "balances": 160 opts.Balances = true 161 case "withdrawals": 162 opts.Withdrawals = true 163 case "articulate": 164 opts.Articulate = true 165 case "cacheTraces": 166 opts.CacheTraces = true 167 case "count": 168 opts.Count = true 169 case "firstRecord": 170 opts.FirstRecord = base.MustParseUint64(value[0]) 171 case "maxRecords": 172 opts.MaxRecords = base.MustParseUint64(value[0]) 173 case "relevant": 174 opts.Relevant = true 175 case "emitter": 176 for _, val := range value { 177 s := strings.Split(val, " ") // may contain space separated items 178 opts.Emitter = append(opts.Emitter, s...) 179 } 180 case "topic": 181 for _, val := range value { 182 s := strings.Split(val, " ") // may contain space separated items 183 opts.Topic = append(opts.Topic, s...) 184 } 185 case "reverted": 186 opts.Reverted = true 187 case "asset": 188 for _, val := range value { 189 s := strings.Split(val, " ") // may contain space separated items 190 opts.Asset = append(opts.Asset, s...) 191 } 192 case "flow": 193 opts.Flow = value[0] 194 case "factory": 195 opts.Factory = true 196 case "unripe": 197 opts.Unripe = true 198 case "reversed": 199 opts.Reversed = true 200 case "noZero": 201 opts.NoZero = true 202 case "firstBlock": 203 opts.FirstBlock = base.MustParseBlknum(value[0]) 204 case "lastBlock": 205 opts.LastBlock = base.MustParseBlknum(value[0]) 206 default: 207 if !copy.Globals.Caps.HasKey(key) { 208 err := validate.Usage("Invalid key ({0}) in {1} route.", key, "export") 209 if opts.BadFlag == nil || opts.BadFlag.Error() > err.Error() { 210 opts.BadFlag = err 211 } 212 } 213 } 214 } 215 opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches()) 216 217 // EXISTING_CODE 218 if len(opts.Addrs) > 0 { 219 addrs := []string{} 220 for _, addr := range opts.Addrs { 221 if validate.IsValidTopic(addr) { 222 opts.Topic = append(opts.Topic, addr) 223 opts.Topics = append(opts.Topics, addr) 224 } else if validate.IsValidFourByte(addr) { 225 opts.Fourbytes = append(opts.Fourbytes, addr) 226 } else { 227 addrs = append(addrs, addr) 228 } 229 } 230 opts.Addrs = addrs 231 } 232 // EXISTING_CODE 233 opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs) 234 opts.Emitter, _ = opts.Conn.GetEnsAddresses(opts.Emitter) 235 opts.Asset, _ = opts.Conn.GetEnsAddresses(opts.Asset) 236 237 return opts 238 } 239 240 // exportFinishParse finishes the parsing for command line invocations. Returns a new ExportOptions. 241 func exportFinishParse(args []string) *ExportOptions { 242 // remove duplicates from args if any (not needed in api mode because the server does it). 243 dedup := map[string]int{} 244 if len(args) > 0 { 245 tmp := []string{} 246 for _, arg := range args { 247 if value := dedup[arg]; value == 0 { 248 tmp = append(tmp, arg) 249 } 250 dedup[arg]++ 251 } 252 args = tmp 253 } 254 255 defFmt := "txt" 256 opts := GetOptions() 257 opts.Conn = opts.Globals.FinishParse(args, opts.getCaches()) 258 259 // EXISTING_CODE 260 for _, arg := range args { 261 if validate.IsValidTopic(arg) { 262 opts.Topic = append(opts.Topic, arg) 263 opts.Topics = append(opts.Topics, arg) 264 } else if validate.IsValidFourByte(arg) { 265 opts.Fourbytes = append(opts.Fourbytes, arg) 266 } else { 267 opts.Addrs = append(opts.Addrs, arg) 268 } 269 } 270 // EXISTING_CODE 271 opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs) 272 opts.Emitter, _ = opts.Conn.GetEnsAddresses(opts.Emitter) 273 opts.Asset, _ = opts.Conn.GetEnsAddresses(opts.Asset) 274 if len(opts.Globals.Format) == 0 || opts.Globals.Format == "none" { 275 opts.Globals.Format = defFmt 276 } 277 278 return opts 279 } 280 281 func GetOptions() *ExportOptions { 282 // EXISTING_CODE 283 // EXISTING_CODE 284 return &defaultExportOptions 285 } 286 287 func getCaps() caps.Capability { 288 var capabilities caps.Capability // capabilities for chifra export 289 capabilities = capabilities.Add(caps.Default) 290 capabilities = capabilities.Add(caps.Caching) 291 capabilities = capabilities.Add(caps.Ether) 292 capabilities = capabilities.Add(caps.Names) 293 // EXISTING_CODE 294 // EXISTING_CODE 295 return capabilities 296 } 297 298 func ResetOptions(testMode bool) { 299 // We want to keep writer between command file calls 300 w := GetOptions().Globals.Writer 301 opts := ExportOptions{} 302 globals.SetDefaults(&opts.Globals) 303 opts.Globals.TestMode = testMode 304 opts.Globals.Writer = w 305 opts.Globals.Caps = getCaps() 306 opts.MaxRecords = 250 307 opts.LastBlock = base.NOPOSN 308 defaultExportOptions = opts 309 } 310 311 func (opts *ExportOptions) getCaches() (caches map[walk.CacheType]bool) { 312 // EXISTING_CODE 313 caches = map[walk.CacheType]bool{ 314 // TODO: Enable neighbors cache 315 walk.Cache_Transactions: true, 316 walk.Cache_Statements: opts.Accounting, 317 walk.Cache_Traces: opts.CacheTraces || (opts.Globals.Cache && (opts.Traces || opts.Neighbors)), 318 } 319 // EXISTING_CODE 320 return 321 } 322 323 // EXISTING_CODE 324 // Validate calls into the opts validateExport routine 325 func (opts *ExportOptions) Validate() error { 326 return opts.validateExport() 327 } 328 329 // TODO: If an abi file is newer than the monitor file - clear the cache 330 // TODO: accounting disallows freshen, apps, logs, receipts, statements, traces, but requires articulate 331 // TODO: accounting must be for one monitor address - why? 332 // TODO: accounting requires node balances - why? 333 // TODO: Used to do this: if any ABI files was newer, re-read abi and re-articulate in cache 334 // TODO: Reconciliation loads traces -- plus it reduplicates the isSuicide, isGeneration, isUncle shit 335 // TODO: If a monitor file is locked, remove the lock and move on (don't read) but don't wait either 336 337 // TODO: In the old C++ code, we used to be able to customize the display of the output with a configuration string. This is a VERY important feature as it captures users 338 // TODO: In the old C++ code, the first address on the command line was `accountedFor`. Is that still true? Or do we now do accounting for multiple addresses? There should be testing. 339 // TODO: In the old C++ code, we used to load knownABIs if we were articulating. Is this still true? Do we load the known ABIs and then overlay them with contract specific clashes? (We should.) 340 // TODO: In the old C++ code, we used to be able to configure certain things - for example, `--cache` is on by default for all queries, `--cache_traces` is on by default, display strings, max_records, max_traces for dDos protection, etc. 341 // TODO: Need much better testing surrounding fourBytes, topics, emitters, relevant, etc. It's also very poorly documented. 342 // TODO: In the old C++ code, the ArticulateAll routine used to identify transactions as token related. Do we still do that? Must we? Why did we do that? 343 344 // EXISTING_CODE