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 }