github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/chunks/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 chunksPkg 10 11 import ( 12 // EXISTING_CODE 13 "encoding/json" 14 "io" 15 "net/http" 16 "net/url" 17 "os" 18 "strings" 19 20 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/internal/globals" 21 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 22 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/caps" 23 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 24 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" 25 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers" 26 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/index" 27 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 28 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 29 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/tslib" 30 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 31 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate" 32 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk" 33 "github.com/ethereum/go-ethereum/common/hexutil" 34 // EXISTING_CODE 35 ) 36 37 // ChunksOptions provides all command options for the chifra chunks command. 38 type ChunksOptions struct { 39 Mode string `json:"mode,omitempty"` // The type of data to process 40 Blocks []string `json:"blocks,omitempty"` // An optional list of blocks to intersect with chunk ranges 41 BlockIds []identifiers.Identifier `json:"blockIds,omitempty"` // Block identifiers 42 Check bool `json:"check,omitempty"` // Check the manifest, index, or blooms for internal consistency 43 Pin bool `json:"pin,omitempty"` // Pin the manifest or each index chunk and bloom 44 Publish bool `json:"publish,omitempty"` // Publish the manifest to the Unchained Index smart contract 45 Publisher string `json:"publisher,omitempty"` // For some query options, the publisher of the index 46 Truncate base.Blknum `json:"truncate,omitempty"` // Truncate the entire index at this block (requires a block identifier) 47 Remote bool `json:"remote,omitempty"` // Prior to processing, retrieve the manifest from the Unchained Index smart contract 48 Belongs []string `json:"belongs,omitempty"` // In index mode only, checks the address(es) for inclusion in the given index chunk 49 Diff bool `json:"diff,omitempty"` // Compare two index portions (see notes) 50 FirstBlock base.Blknum `json:"firstBlock,omitempty"` // First block to process (inclusive) 51 LastBlock base.Blknum `json:"lastBlock,omitempty"` // Last block to process (inclusive) 52 MaxAddrs uint64 `json:"maxAddrs,omitempty"` // The max number of addresses to process in a given chunk 53 Deep bool `json:"deep,omitempty"` // If true, dig more deeply during checking (manifest only) 54 Rewrite bool `json:"rewrite,omitempty"` // For the --pin --deep mode only, writes the manifest back to the index folder (see notes) 55 List bool `json:"list,omitempty"` // For the pins mode only, list the remote pins 56 Unpin bool `json:"unpin,omitempty"` // For the pins mode only, if true reads local ./unpins file for valid CIDs and remotely unpins each (skips non-CIDs) 57 Count bool `json:"count,omitempty"` // For certain modes only, display the count of records 58 Tag string `json:"tag,omitempty"` // Visits each chunk and updates the headers with the supplied version string (vX.Y.Z-str) 59 Sleep float64 `json:"sleep,omitempty"` // For --remote pinning only, seconds to sleep between API calls 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 PublisherAddr base.Address `json:"-"` 65 // EXISTING_CODE 66 } 67 68 var defaultChunksOptions = ChunksOptions{ 69 Truncate: base.NOPOSN, 70 LastBlock: base.NOPOSN, 71 MaxAddrs: base.NOPOS, 72 } 73 74 // testLog is used only during testing to export the options for this test case. 75 func (opts *ChunksOptions) testLog() { 76 logger.TestLog(len(opts.Mode) > 0, "Mode: ", opts.Mode) 77 logger.TestLog(len(opts.Blocks) > 0, "Blocks: ", opts.Blocks) 78 logger.TestLog(opts.Check, "Check: ", opts.Check) 79 logger.TestLog(opts.Pin, "Pin: ", opts.Pin) 80 logger.TestLog(opts.Publish, "Publish: ", opts.Publish) 81 logger.TestLog(len(opts.Publisher) > 0, "Publisher: ", opts.Publisher) 82 logger.TestLog(opts.Truncate != base.NOPOSN, "Truncate: ", opts.Truncate) 83 logger.TestLog(opts.Remote, "Remote: ", opts.Remote) 84 logger.TestLog(len(opts.Belongs) > 0, "Belongs: ", opts.Belongs) 85 logger.TestLog(opts.Diff, "Diff: ", opts.Diff) 86 logger.TestLog(opts.FirstBlock != 0, "FirstBlock: ", opts.FirstBlock) 87 logger.TestLog(opts.LastBlock != base.NOPOSN && opts.LastBlock != 0, "LastBlock: ", opts.LastBlock) 88 logger.TestLog(opts.MaxAddrs != base.NOPOS, "MaxAddrs: ", opts.MaxAddrs) 89 logger.TestLog(opts.Deep, "Deep: ", opts.Deep) 90 logger.TestLog(opts.Rewrite, "Rewrite: ", opts.Rewrite) 91 logger.TestLog(opts.List, "List: ", opts.List) 92 logger.TestLog(opts.Unpin, "Unpin: ", opts.Unpin) 93 logger.TestLog(opts.Count, "Count: ", opts.Count) 94 logger.TestLog(len(opts.Tag) > 0, "Tag: ", opts.Tag) 95 logger.TestLog(opts.Sleep != float64(0.0), "Sleep: ", opts.Sleep) 96 opts.Conn.TestLog(opts.getCaches()) 97 opts.Globals.TestLog() 98 } 99 100 // String implements the Stringer interface 101 func (opts *ChunksOptions) String() string { 102 b, _ := json.MarshalIndent(opts, "", " ") 103 return string(b) 104 } 105 106 // chunksFinishParseApi finishes the parsing for server invocations. Returns a new ChunksOptions. 107 func chunksFinishParseApi(w http.ResponseWriter, r *http.Request) *ChunksOptions { 108 values := r.URL.Query() 109 if r.Header.Get("User-Agent") == "testRunner" { 110 values.Set("testRunner", "true") 111 } 112 return ChunksFinishParseInternal(w, values) 113 } 114 115 func ChunksFinishParseInternal(w io.Writer, values url.Values) *ChunksOptions { 116 copy := defaultChunksOptions 117 copy.Globals.Caps = getCaps() 118 opts := © 119 opts.Truncate = base.NOPOSN 120 opts.LastBlock = base.NOPOSN 121 opts.MaxAddrs = base.NOPOS 122 for key, value := range values { 123 switch key { 124 case "mode": 125 opts.Mode = value[0] 126 case "blocks": 127 for _, val := range value { 128 s := strings.Split(val, " ") // may contain space separated items 129 opts.Blocks = append(opts.Blocks, s...) 130 } 131 case "check": 132 opts.Check = true 133 case "pin": 134 opts.Pin = true 135 case "publish": 136 opts.Publish = true 137 case "publisher": 138 opts.Publisher = value[0] 139 case "truncate": 140 opts.Truncate = base.MustParseBlknum(value[0]) 141 case "remote": 142 opts.Remote = true 143 case "belongs": 144 for _, val := range value { 145 s := strings.Split(val, " ") // may contain space separated items 146 opts.Belongs = append(opts.Belongs, s...) 147 } 148 case "diff": 149 opts.Diff = true 150 case "firstBlock": 151 opts.FirstBlock = base.MustParseBlknum(value[0]) 152 case "lastBlock": 153 opts.LastBlock = base.MustParseBlknum(value[0]) 154 case "maxAddrs": 155 opts.MaxAddrs = base.MustParseUint64(value[0]) 156 case "deep": 157 opts.Deep = true 158 case "rewrite": 159 opts.Rewrite = true 160 case "list": 161 opts.List = true 162 case "unpin": 163 opts.Unpin = true 164 case "count": 165 opts.Count = true 166 case "tag": 167 opts.Tag = value[0] 168 case "sleep": 169 opts.Sleep = base.MustParseFloat64(value[0]) 170 default: 171 if !copy.Globals.Caps.HasKey(key) { 172 err := validate.Usage("Invalid key ({0}) in {1} route.", key, "chunks") 173 if opts.BadFlag == nil || opts.BadFlag.Error() > err.Error() { 174 opts.BadFlag = err 175 } 176 } 177 } 178 } 179 opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches()) 180 opts.Publisher, _ = opts.Conn.GetEnsAddress(config.GetPublisher(opts.Publisher)) 181 opts.PublisherAddr = base.HexToAddress(opts.Publisher) 182 183 // EXISTING_CODE 184 // EXISTING_CODE 185 opts.Belongs, _ = opts.Conn.GetEnsAddresses(opts.Belongs) 186 187 return opts 188 } 189 190 // chunksFinishParse finishes the parsing for command line invocations. Returns a new ChunksOptions. 191 func chunksFinishParse(args []string) *ChunksOptions { 192 // remove duplicates from args if any (not needed in api mode because the server does it). 193 dedup := map[string]int{} 194 if len(args) > 0 { 195 tmp := []string{} 196 for _, arg := range args { 197 if value := dedup[arg]; value == 0 { 198 tmp = append(tmp, arg) 199 } 200 dedup[arg]++ 201 } 202 args = tmp 203 } 204 205 defFmt := "txt" 206 opts := GetOptions() 207 opts.Conn = opts.Globals.FinishParse(args, opts.getCaches()) 208 opts.Publisher, _ = opts.Conn.GetEnsAddress(config.GetPublisher(opts.Publisher)) 209 opts.PublisherAddr = base.HexToAddress(opts.Publisher) 210 211 // EXISTING_CODE 212 if len(args) > 0 { 213 opts.Mode = args[0] 214 for i, arg := range args { 215 if i > 0 { 216 if base.IsValidAddress(arg) { 217 opts.Belongs = append(opts.Belongs, arg) 218 } else { 219 opts.Blocks = append(opts.Blocks, arg) 220 } 221 } 222 } 223 } 224 if opts.Truncate == 0 { 225 opts.Truncate = base.NOPOSN 226 } 227 if opts.LastBlock == 0 { 228 opts.LastBlock = base.NOPOSN 229 } 230 if opts.MaxAddrs == 0 { 231 opts.MaxAddrs = base.NOPOS 232 } 233 getDef := func(def string) string { 234 if opts.Truncate != base.NOPOSN || len(opts.Belongs) > 0 || opts.Pin { 235 return "json" 236 } 237 return def 238 } 239 defFmt = getDef(defFmt) 240 // EXISTING_CODE 241 opts.Belongs, _ = opts.Conn.GetEnsAddresses(opts.Belongs) 242 if len(opts.Globals.Format) == 0 || opts.Globals.Format == "none" { 243 opts.Globals.Format = defFmt 244 } 245 246 return opts 247 } 248 249 func GetOptions() *ChunksOptions { 250 // EXISTING_CODE 251 // EXISTING_CODE 252 return &defaultChunksOptions 253 } 254 255 func getCaps() caps.Capability { 256 var capabilities caps.Capability // capabilities for chifra chunks 257 capabilities = capabilities.Add(caps.Default) 258 // EXISTING_CODE 259 // EXISTING_CODE 260 return capabilities 261 } 262 263 func ResetOptions(testMode bool) { 264 // We want to keep writer between command file calls 265 w := GetOptions().Globals.Writer 266 opts := ChunksOptions{} 267 globals.SetDefaults(&opts.Globals) 268 opts.Globals.TestMode = testMode 269 opts.Globals.Writer = w 270 opts.Globals.Caps = getCaps() 271 opts.Truncate = base.NOPOSN 272 opts.LastBlock = base.NOPOSN 273 opts.MaxAddrs = base.NOPOS 274 defaultChunksOptions = opts 275 } 276 277 func (opts *ChunksOptions) getCaches() (caches map[walk.CacheType]bool) { 278 // EXISTING_CODE 279 // EXISTING_CODE 280 return 281 } 282 283 // EXISTING_CODE 284 func (opts *ChunksOptions) shouldShow(obj types.AddrRecord) bool { 285 if opts.Mode == "addresses" || opts.Mode == "appearances" { 286 return opts.Globals.Verbose 287 } 288 289 for _, addr := range opts.Belongs { 290 if hexutil.Encode(obj.Address.Bytes()) == addr { 291 return true 292 } 293 } 294 return false 295 } 296 297 func GetChunkStats(chain, path string) (s types.ChunkStats, err error) { 298 chunk, err := index.OpenChunk(path, true /* check */) 299 if err != nil && !os.IsNotExist(err) { 300 return s, err 301 } 302 defer chunk.Close() 303 304 rng := chunk.Range 305 s = types.ChunkStats{ 306 Range: rng.String(), 307 NBlocks: uint64(chunk.Range.Last - chunk.Range.First + 1), 308 NAddrs: uint64(chunk.Index.Header.AddressCount), 309 NApps: uint64(chunk.Index.Header.AppearanceCount), 310 NBlooms: uint64(chunk.Bloom.Count), 311 BloomSz: uint64(file.FileSize(index.ToBloomPath(path))), 312 ChunkSz: uint64(file.FileSize(index.ToIndexPath(path))), 313 RecWid: 4 + index.BLOOM_WIDTH_IN_BYTES, 314 } 315 rd := tslib.RangeToBounds(chain, &rng) 316 s.RangeDates = &rd 317 318 if s.NBlocks > 0 { 319 s.AddrsPerBlock = float64(s.NAddrs) / float64(s.NBlocks) 320 s.AppsPerBlock = float64(s.NApps) / float64(s.NBlocks) 321 } 322 323 if s.NAddrs > 0 { 324 s.AppsPerAddr = float64(s.NApps) / float64(s.NAddrs) 325 } 326 327 if s.BloomSz > 0 { 328 s.Ratio = float64(s.ChunkSz) / float64(s.BloomSz) 329 } 330 331 return s, nil 332 } 333 334 // EXISTING_CODE