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  }