github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/walk/walk.go (about) 1 // Copyright 2021 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 package walk 6 7 import ( 8 "context" 9 "io/fs" 10 "os" 11 "path/filepath" 12 "strings" 13 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 15 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" 17 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 18 ) 19 20 type CacheType uint 21 22 const ( 23 Cache_NotACache CacheType = iota 24 Cache_Abis 25 Cache_Monitors 26 Cache_Names 27 Cache_Tmp 28 29 Cache_Blocks 30 Cache_Logs 31 Cache_Receipts 32 Cache_Results 33 Cache_Slurps 34 Cache_State 35 Cache_Statements 36 Cache_Tokens 37 Cache_Traces 38 Cache_Transactions 39 Cache_Withdrawals 40 41 Index_Bloom 42 Index_Final 43 Index_Ripe 44 Index_Staging 45 Index_Unripe 46 Index_Maps 47 48 Config 49 Regular 50 ) 51 52 var cacheTypeToName = map[CacheType]string{ 53 Cache_NotACache: "unknown", 54 Cache_Abis: "abis", 55 Cache_Monitors: "monitors", 56 Cache_Names: "names", 57 Cache_Tmp: "tmp", 58 Cache_Blocks: "blocks", 59 Cache_Logs: "logs", 60 Cache_Receipts: "receipts", 61 Cache_Results: "results", 62 Cache_Slurps: "slurps", 63 Cache_State: "state", 64 Cache_Statements: "statements", 65 Cache_Tokens: "tokens", 66 Cache_Traces: "traces", 67 Cache_Transactions: "transactions", 68 Cache_Withdrawals: "withdrawals", 69 Index_Bloom: "bloom", 70 Index_Final: "index", 71 Index_Ripe: "ripe", 72 Index_Staging: "staging", 73 Index_Unripe: "unripe", 74 Index_Maps: "neighbors", 75 Config: "config", 76 Regular: "regular", 77 } 78 79 // CacheTypeToFolder is a map of cache types to the folder name (also, it acts as the mode in chifra status) 80 var CacheTypeToFolder = map[CacheType]string{ 81 Cache_NotACache: "unknown", 82 Cache_Abis: "abis", 83 Cache_Monitors: "monitors", 84 Cache_Names: "names", 85 Cache_Tmp: "tmp", 86 Cache_Blocks: "blocks", 87 Cache_Logs: "logs", 88 Cache_Receipts: "receipts", 89 Cache_Results: "results", 90 Cache_Slurps: "slurps", 91 Cache_State: "state", 92 Cache_Statements: "statements", 93 Cache_Tokens: "tokens", 94 Cache_Traces: "traces", 95 Cache_Transactions: "transactions", 96 Cache_Withdrawals: "withdrawals", 97 Index_Bloom: "blooms", 98 Index_Final: "finalized", 99 Index_Ripe: "ripe", 100 Index_Staging: "staging", 101 Index_Unripe: "unripe", 102 Index_Maps: "maps", 103 Config: "config", 104 Regular: "regular", 105 } 106 107 var cacheTypeToExt = map[CacheType]string{ 108 Cache_NotACache: "unknown", 109 Cache_Abis: "json", 110 Cache_Monitors: "mon.bin", 111 Cache_Names: "bin", 112 Cache_Tmp: "", 113 Cache_Blocks: "bin", 114 Cache_Logs: "bin", 115 Cache_Receipts: "bin", 116 Cache_Results: "bin", 117 Cache_Slurps: "bin", 118 Cache_State: "bin", 119 Cache_Statements: "bin", 120 Cache_Tokens: "bin", 121 Cache_Traces: "bin", 122 Cache_Transactions: "bin", 123 Cache_Withdrawals: "bin", 124 Index_Bloom: "bloom", 125 Index_Final: "bin", 126 Index_Ripe: "txt", 127 Index_Staging: "txt", 128 Index_Unripe: "txt", 129 Index_Maps: "bin", 130 Config: "toml", 131 Regular: "", 132 } 133 134 func (ct CacheType) String() string { 135 return cacheTypeToName[ct] 136 } 137 138 func IsCacheType(path string, cT CacheType, checkExt bool) bool { 139 if !strings.Contains(path, CacheTypeToFolder[cT]) { 140 return false 141 } 142 if checkExt && !strings.HasSuffix(path, cacheTypeToExt[cT]) { 143 return false 144 } 145 return true 146 } 147 148 func GetRootPathFromCacheType(chain string, cacheType CacheType) string { 149 switch cacheType { 150 case Cache_Abis: 151 fallthrough 152 case Cache_Monitors: 153 fallthrough 154 case Cache_Names: 155 fallthrough 156 case Cache_Tmp: 157 return filepath.Join(config.PathToCache(chain), CacheTypeToFolder[cacheType]) 158 159 case Cache_Blocks: 160 fallthrough 161 case Cache_Logs: 162 fallthrough 163 case Cache_Receipts: 164 fallthrough 165 case Cache_Results: 166 fallthrough 167 case Cache_Slurps: 168 fallthrough 169 case Cache_State: 170 fallthrough 171 case Cache_Statements: 172 fallthrough 173 case Cache_Tokens: 174 fallthrough 175 case Cache_Traces: 176 fallthrough 177 case Cache_Transactions: 178 fallthrough 179 case Cache_Withdrawals: 180 return filepath.Join(config.PathToCache(chain), "v1", CacheTypeToFolder[cacheType]) 181 182 case Index_Bloom: 183 fallthrough 184 case Index_Final: 185 fallthrough 186 case Index_Ripe: 187 fallthrough 188 case Index_Staging: 189 fallthrough 190 case Index_Unripe: 191 fallthrough 192 case Index_Maps: 193 return filepath.Join(config.PathToIndex(chain), CacheTypeToFolder[cacheType]) 194 case Config: 195 return config.PathToRootConfig() 196 case Cache_NotACache: 197 fallthrough 198 default: 199 logger.Fatal("should not happen ==> in paths.go") 200 } 201 202 logger.Fatal("should not happen ==> in paths.go") 203 return "" 204 } 205 206 func WalkCacheFolder(ctx context.Context, chain string, cacheType CacheType, data interface{}, filenameChan chan<- CacheFileInfo) { 207 path := GetRootPathFromCacheType(chain, cacheType) 208 walkFolder(ctx, path, cacheType, data, filenameChan) 209 } 210 211 func WalkConfigFolders(ctx context.Context, data interface{}, filenameChan chan<- CacheFileInfo) { 212 path := config.PathToRootConfig() 213 walkFolder(ctx, path, Config, data, filenameChan) 214 } 215 216 func WalkFolder(ctx context.Context, path string, data interface{}, filenameChan chan<- CacheFileInfo) { 217 walkFolder(ctx, path, Regular, data, filenameChan) 218 } 219 220 func walkFolder(ctx context.Context, path string, cacheType CacheType, data interface{}, filenameChan chan<- CacheFileInfo) { 221 defer func() { 222 filenameChan <- CacheFileInfo{Type: Cache_NotACache} 223 }() 224 225 _ = filepath.Walk(path, func(path string, info fs.FileInfo, err error) error { 226 if err != nil { 227 // If the scraper is running, this will sometimes send an error for a file, for example, that existed 228 // when it was first seen, but the scraper deletes before this call. We ignore any file system errors 229 // this routine, but if we experience problems, we can uncomment this line 230 // fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err) 231 return err 232 } 233 234 if info.IsDir() { 235 filenameChan <- CacheFileInfo{Type: cacheType, Path: path, IsDir: true, Data: data} 236 237 } else { 238 // TODO: This does not need to be part of walker. It could be in the caller and sent through the data pointer 239 rng := base.RangeFromFilename(path) 240 filenameChan <- CacheFileInfo{Type: cacheType, Path: path, FileRange: rng, Data: data} 241 } 242 243 select { 244 case <-ctx.Done(): 245 return ctx.Err() 246 default: 247 } 248 249 return nil 250 }) 251 } 252 253 func CacheTypesFromStringSlice(strs []string) []CacheType { 254 haveit := map[string]bool{} // removes dups 255 var types []CacheType 256 for _, str := range strs { 257 if !haveit[str] { 258 haveit[str] = true 259 switch str { 260 case "abis": 261 types = append(types, Cache_Abis) 262 case "monitors": 263 types = append(types, Cache_Monitors) 264 case "names": 265 types = append(types, Cache_Names) 266 case "tmp": 267 types = append(types, Cache_Tmp) 268 269 case "blocks": 270 types = append(types, Cache_Blocks) 271 case "logs": 272 types = append(types, Cache_Logs) 273 case "receipts": 274 types = append(types, Cache_Receipts) 275 case "results": 276 types = append(types, Cache_Results) 277 case "slurps": 278 types = append(types, Cache_Slurps) 279 case "state": 280 types = append(types, Cache_State) 281 case "statements": 282 types = append(types, Cache_Statements) 283 case "tokens": 284 types = append(types, Cache_Tokens) 285 case "traces": 286 types = append(types, Cache_Traces) 287 case "transactions": 288 types = append(types, Cache_Transactions) 289 case "withdrawals": 290 types = append(types, Cache_Withdrawals) 291 292 case "blooms": 293 types = append(types, Index_Bloom) 294 case "index": 295 fallthrough 296 case "finalized": 297 types = append(types, Index_Final) 298 case "ripe": 299 types = append(types, Index_Ripe) 300 case "staging": 301 types = append(types, Index_Staging) 302 case "unripe": 303 types = append(types, Index_Unripe) 304 case "maps": 305 types = append(types, Index_Maps) 306 case "some": 307 types = append(types, Index_Final) 308 types = append(types, Cache_Monitors) 309 types = append(types, Cache_Names) 310 types = append(types, Cache_Abis) 311 types = append(types, Cache_Slurps) 312 case "all": 313 types = append(types, Index_Bloom) 314 types = append(types, Index_Final) 315 types = append(types, Index_Staging) 316 types = append(types, Index_Unripe) 317 types = append(types, Cache_Abis) 318 types = append(types, Cache_Monitors) 319 types = append(types, Cache_Names) 320 types = append(types, Cache_Blocks) 321 types = append(types, Cache_Logs) 322 types = append(types, Cache_Receipts) 323 types = append(types, Cache_Results) 324 types = append(types, Cache_Slurps) 325 types = append(types, Cache_State) 326 types = append(types, Cache_Statements) 327 types = append(types, Cache_Tokens) 328 types = append(types, Cache_Traces) 329 types = append(types, Cache_Transactions) 330 types = append(types, Cache_Withdrawals) 331 } 332 } 333 } 334 return types 335 } 336 337 func GetCacheItem(chain string, testMode bool, cT CacheType, cacheInfo *CacheFileInfo) (map[string]any, error) { 338 date := "--fileDate--" 339 info, err := os.Stat(cacheInfo.Path) 340 if !testMode && err == nil { 341 date = info.ModTime().Format("2006-01-02 15:04:05") 342 } 343 344 size := file.FileSize(cacheInfo.Path) 345 if testMode { 346 size = 123456789 347 } 348 349 display := cacheInfo.Path 350 display = filepath.Clean(strings.ReplaceAll(display, config.PathToCache(chain)+"/", "./")) 351 display = filepath.Clean(strings.ReplaceAll(display, config.PathToIndex(chain)+"/", "./")) 352 353 switch cT { 354 case Index_Maps: 355 fallthrough 356 case Index_Bloom: 357 fallthrough 358 case Index_Final: 359 if testMode { 360 display = strings.Replace(cacheInfo.Path, config.PathToIndex(chain)+"/", "$indexPath/", 1) 361 } 362 return map[string]any{ 363 // "bloomSizeBytes": file.FileSize(index.ToBloomPath(cacheInfo.Path)), 364 "fileDate": date, 365 "filename": display, 366 "firstApp": cacheInfo.FileRange.First, 367 "firstTs": cacheInfo.TsRange.First, 368 // "indexSizeBytes": file.FileSize(index.ToIndexPath(cacheInfo.Path)), 369 "itemType": WalkCacheName(cT) + "Item", 370 "latestApp": cacheInfo.FileRange.Last, 371 "latestTs": cacheInfo.TsRange.Last, 372 }, nil 373 case Cache_Monitors: 374 fallthrough 375 case Cache_Slurps: 376 fallthrough 377 case Cache_Abis: 378 address := "" 379 parts := strings.Split(cacheInfo.Path, string(os.PathSeparator)) 380 for _, part := range parts { 381 if strings.HasPrefix(part, "0x") { 382 address = part 383 break 384 } 385 } 386 if testMode { 387 display = strings.Replace(cacheInfo.Path, config.PathToCache(chain)+"/", "$cachePath/", 1) 388 display = strings.Replace(display, address, "--address--", -1) 389 address = "--address--" 390 } 391 ret := map[string]any{ 392 "address": address, 393 "fileDate": date, 394 "filename": display, 395 "itemType": WalkCacheName(cT) + "Item", 396 "sizeInBytes": size, 397 } 398 if cT == Cache_Monitors { 399 ret["nRecords"] = size / 8 // index.AppRecordWidth - FAST 400 } 401 return ret, nil 402 default: 403 if testMode { 404 display = "$cachePath/data-model/file.bin" 405 } 406 return map[string]any{ 407 "fileDate": date, 408 "filename": display, 409 "itemType": WalkCacheName(cT) + "Item", 410 "sizeInBytes": size, 411 }, nil 412 } 413 } 414 415 func WalkCacheName(ct CacheType) string { 416 // TODO: Names of caches, names of folders, names of commands are all different. This is a mess. 417 ret := CacheTypeToFolder[ct] + "Cache" 418 ret = strings.Replace(ret, "blooms", "bloom", -1) 419 ret = strings.Replace(ret, "finalized", "index", -1) 420 return ret 421 } 422 423 type CacheFileInfo struct { 424 Type CacheType 425 FileRange base.FileRange 426 TsRange base.TimestampRange 427 Path string 428 IsDir bool 429 Data interface{} 430 }