github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/blocks/handle_uncles.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 blocksPkg 6 7 import ( 8 "context" 9 "fmt" 10 "sort" 11 12 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 13 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers" 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 15 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output" 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 17 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" 18 ) 19 20 func (opts *BlocksOptions) HandleUncles(rCtx *output.RenderCtx) error { 21 chain := opts.Globals.Chain 22 testMode := opts.Globals.TestMode 23 nErrors := 0 24 25 fetchData := func(modelChan chan types.Modeler, errorChan chan error) { 26 apps, _, err := identifiers.IdsToApps(chain, opts.BlockIds) 27 if err != nil { 28 errorChan <- err 29 rCtx.Cancel() 30 } 31 32 if sliceOfMaps, cnt, err := types.AsSliceOfMaps[types.LightBlock](apps, false); err != nil { 33 errorChan <- err 34 rCtx.Cancel() 35 36 } else if cnt == 0 { 37 errorChan <- fmt.Errorf("no blocks found for the query") 38 rCtx.Cancel() 39 40 } else { 41 showProgress := opts.Globals.ShowProgress() 42 bar := logger.NewBar(logger.BarOptions{ 43 Enabled: showProgress, 44 Total: int64(cnt), 45 }) 46 47 for _, thisMap := range sliceOfMaps { 48 if rCtx.WasCanceled() { 49 return 50 } 51 52 for app := range thisMap { 53 thisMap[app] = new(types.LightBlock) 54 } 55 56 items := make([]*types.Block, 0, len(thisMap)) 57 iterFunc := func(app types.Appearance, value *types.LightBlock) error { 58 bn := base.Blknum(app.BlockNumber) 59 if uncles, err := opts.Conn.GetUncleBodiesByNumber(bn); err != nil { 60 delete(thisMap, app) 61 return err 62 } else { 63 for _, uncle := range uncles { 64 items = append(items, &uncle) 65 } 66 bar.Tick() 67 return nil 68 } 69 } 70 71 iterErrorChan := make(chan error) 72 iterCtx, iterCancel := context.WithCancel(context.Background()) 73 defer iterCancel() 74 go utils.IterateOverMap(iterCtx, iterErrorChan, thisMap, iterFunc) 75 for err := range iterErrorChan { 76 if !testMode || nErrors == 0 { 77 errorChan <- err 78 nErrors++ 79 } 80 } 81 82 sort.Slice(items, func(i, j int) bool { 83 if items[i].BlockNumber == items[j].BlockNumber { 84 return items[i].Hash.Hex() < items[j].Hash.Hex() 85 } 86 return items[i].BlockNumber < items[j].BlockNumber 87 }) 88 89 for _, item := range items { 90 modelChan <- item 91 } 92 } 93 bar.Finish(true /* newLine */) 94 } 95 } 96 97 extraOpts := map[string]any{ 98 "uncles": opts.Uncles, 99 "articulate": opts.Articulate, 100 } 101 102 return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts)) 103 }