github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/chunks/handle_check.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 chunksPkg
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"path/filepath"
    11  	"sort"
    12  
    13  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    14  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config"
    15  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/history"
    16  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    17  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/manifest"
    18  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output"
    19  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    20  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk"
    21  )
    22  
    23  // HandleCheck looks at three different arrays: index files on disc, manifest on disc,
    24  // and manifest in the smart contract. It tries to check these three sources for
    25  // cosnsistency. Smart contract rules, so it is checked more thoroughly.
    26  func (opts *ChunksOptions) HandleCheck(rCtx *output.RenderCtx, blockNums []base.Blknum) error {
    27  	err, _ := opts.check(rCtx, blockNums, false /* silent */)
    28  	return err
    29  }
    30  
    31  // check provides internal checks against the index used from the command line and internally before pinning
    32  func (opts *ChunksOptions) check(rCtx *output.RenderCtx, blockNums []base.Blknum, silent bool) (error, bool) {
    33  	chain := opts.Globals.Chain
    34  
    35  	maxTestItems := 10
    36  	filenameChan := make(chan walk.CacheFileInfo)
    37  
    38  	var nRoutines = 1
    39  	go walk.WalkCacheFolder(rCtx.Ctx, chain, walk.Index_Bloom, nil, filenameChan)
    40  
    41  	fileNames := []string{}
    42  	for result := range filenameChan {
    43  		switch result.Type {
    44  		case walk.Index_Bloom:
    45  			skip := (opts.Globals.TestMode && len(fileNames) > maxTestItems) || !walk.IsCacheType(result.Path, walk.Index_Bloom, true /* checkExt */)
    46  			if !skip {
    47  				hit := false
    48  				for _, block := range blockNums {
    49  					h := result.FileRange.IntersectsB(block)
    50  					hit = hit || h
    51  					if hit {
    52  						break
    53  					}
    54  				}
    55  				if len(blockNums) == 0 || hit {
    56  					fileNames = append(fileNames, result.Path)
    57  				}
    58  			}
    59  		case walk.Cache_NotACache:
    60  			nRoutines--
    61  			if nRoutines == 0 {
    62  				close(filenameChan)
    63  			}
    64  		default:
    65  			logger.Fatal("should not happen ==> only traverse the bloom folder")
    66  		}
    67  	}
    68  
    69  	if len(fileNames) == 0 {
    70  		msg := fmt.Sprint("No files found to check in", config.PathToIndex(chain))
    71  		return errors.New(msg), false
    72  	}
    73  
    74  	sort.Slice(fileNames, func(i, j int) bool {
    75  		return fileNames[i] < fileNames[j]
    76  	})
    77  
    78  	cacheManifest, err := manifest.LoadManifest(chain, opts.PublisherAddr, manifest.LocalCache)
    79  	if err != nil {
    80  		return err, false
    81  	}
    82  
    83  	remoteManifest, err := manifest.LoadManifest(chain, opts.PublisherAddr, manifest.TempContract)
    84  	if err != nil {
    85  		return err, false
    86  	}
    87  	historyFile := filepath.Join(config.PathToRootConfig(), "unchained.txt")
    88  	saved := history.FromHistory(historyFile, "headerVersion")
    89  	defer func() {
    90  		_ = history.ToHistory(historyFile, "headerVersion", saved)
    91  	}()
    92  	_ = history.ToHistory(historyFile, "headerVersion", remoteManifest.Version)
    93  	logger.InfoTable("Existing version:", saved)
    94  	logger.InfoTable("Remote version:", remoteManifest.Version)
    95  
    96  	// a string array of the actual files in the index
    97  	fnArray := []string{}
    98  	for _, fileName := range fileNames {
    99  		rng := base.RangeFromFilename(fileName)
   100  		fnArray = append(fnArray, rng.String())
   101  	}
   102  	sort.Slice(fnArray, func(i, j int) bool {
   103  		return fnArray[i] < fnArray[j]
   104  	})
   105  
   106  	// a string array of the ranges in the local manifest
   107  	cacheArray := []string{}
   108  	for _, chunk := range cacheManifest.Chunks {
   109  		cacheArray = append(cacheArray, chunk.Range)
   110  	}
   111  	sort.Slice(cacheArray, func(i, j int) bool {
   112  		return cacheArray[i] < cacheArray[j]
   113  	})
   114  
   115  	// a string array of the ranges from the remote manifest
   116  	remoteArray := []string{}
   117  	for _, chunk := range remoteManifest.Chunks {
   118  		remoteArray = append(remoteArray, chunk.Range)
   119  	}
   120  	sort.Slice(remoteArray, func(i, j int) bool {
   121  		return remoteArray[i] < remoteArray[j]
   122  	})
   123  
   124  	reports := []types.ReportCheck{}
   125  
   126  	allowMissing := config.GetScrape(chain).AllowMissing
   127  
   128  	seq := types.ReportCheck{Reason: "Filenames sequential"}
   129  	if err := opts.CheckSequential(fileNames, cacheArray, remoteArray, allowMissing, &seq); err != nil {
   130  		return err, false
   131  	}
   132  	reports = append(reports, seq)
   133  
   134  	intern := types.ReportCheck{Reason: "Internally consistent"}
   135  	if err := opts.CheckInternal(fileNames, blockNums, &intern); err != nil {
   136  		return err, false
   137  	}
   138  	reports = append(reports, intern)
   139  
   140  	version := types.ReportCheck{Reason: "Correct version"}
   141  	if err := opts.CheckVersion(fileNames, blockNums, &version); err != nil {
   142  		return err, false
   143  	}
   144  	reports = append(reports, version)
   145  
   146  	con := types.ReportCheck{Reason: "Consistent hashes"}
   147  	if err := opts.CheckHashes(cacheManifest, remoteManifest, &con); err != nil {
   148  		return err, false
   149  	}
   150  	reports = append(reports, con)
   151  
   152  	sizes := types.ReportCheck{Reason: "Check file sizes"}
   153  	if err := opts.CheckSizes(fileNames, blockNums, cacheManifest, remoteManifest, &sizes); err != nil {
   154  		return err, false
   155  	}
   156  	reports = append(reports, sizes)
   157  
   158  	// are all the hashes present?
   159  	contentCheck := types.ReportCheck{}
   160  	contentCheck.Reason = "Remote manifest contents"
   161  	if err := opts.CheckManContents(remoteManifest, &contentCheck); err != nil {
   162  		return err, false
   163  	}
   164  	reports = append(reports, contentCheck)
   165  
   166  	contentCheck.Reason = "Local manifest contents"
   167  	if err := opts.CheckManContents(remoteManifest, &contentCheck); err != nil {
   168  		return err, false
   169  	}
   170  	reports = append(reports, contentCheck)
   171  
   172  	// compare with çached manifest with files on disc
   173  	d2c := types.ReportCheck{Reason: "Disc files to cached manifest"}
   174  	if err := opts.CheckManifest(fnArray, cacheArray, &d2c); err != nil {
   175  		return err, false
   176  	}
   177  	reports = append(reports, d2c)
   178  
   179  	// compare with remote manifest with files on disc
   180  	d2r := types.ReportCheck{Reason: "Disc files to remote manifest"}
   181  	if err := opts.CheckManifest(fnArray, remoteArray, &d2r); err != nil {
   182  		return err, false
   183  	}
   184  	reports = append(reports, d2r)
   185  
   186  	// compare remote manifest to cached manifest
   187  	r2c := types.ReportCheck{Reason: "Remote manifest to cached manifest"}
   188  	if err := opts.CheckManifest(remoteArray, cacheArray, &r2c); err != nil {
   189  		return err, false
   190  	}
   191  	reports = append(reports, r2c)
   192  
   193  	if opts.Deep {
   194  		deep := types.ReportCheck{Reason: "Deep checks for " + opts.Mode}
   195  		if err := opts.CheckDeep(cacheManifest, &deep); err != nil {
   196  			return err, false
   197  		}
   198  		reports = append(reports, deep)
   199  	}
   200  
   201  	nFailed := 0
   202  	for i := 0; i < len(reports); i++ {
   203  		reports[i].FailedCnt = reports[i].CheckedCnt - reports[i].PassedCnt
   204  		if reports[i].FailedCnt == 0 {
   205  			reports[i].Result = "passed"
   206  		} else {
   207  			reports[i].Result = "failed"
   208  			reports[i].SkippedCnt = reports[i].VisitedCnt - reports[i].CheckedCnt
   209  		}
   210  		nFailed += int(reports[i].FailedCnt)
   211  	}
   212  
   213  	fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
   214  		for _, report := range reports {
   215  			if !silent {
   216  				modelChan <- &report
   217  			}
   218  		}
   219  	}
   220  
   221  	err = output.StreamMany(rCtx, fetchData, opts.Globals.OutputOpts())
   222  	return err, nFailed == 0
   223  }