github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/blocks/handle_traces.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/articulate" 13 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers" 15 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output" 17 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 18 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" 19 ) 20 21 func (opts *BlocksOptions) HandleTraces(rCtx *output.RenderCtx) error { 22 chain := opts.Globals.Chain 23 testMode := opts.Globals.TestMode 24 nErrors := 0 25 26 abiCache := articulate.NewAbiCache(opts.Conn, opts.Articulate) 27 28 fetchData := func(modelChan chan types.Modeler, errorChan chan error) { 29 apps, _, err := identifiers.IdsToApps(chain, opts.BlockIds) 30 if err != nil { 31 errorChan <- err 32 rCtx.Cancel() 33 } 34 35 if sliceOfMaps, cnt, err := types.AsSliceOfMaps[types.Transaction](apps, false); err != nil { 36 errorChan <- err 37 rCtx.Cancel() 38 39 } else if cnt == 0 { 40 errorChan <- fmt.Errorf("no blocks found for the query") 41 rCtx.Cancel() 42 43 } else { 44 showProgress := opts.Globals.ShowProgress() 45 bar := logger.NewBar(logger.BarOptions{ 46 Enabled: showProgress, 47 Total: int64(cnt), 48 }) 49 50 for _, thisMap := range sliceOfMaps { 51 if rCtx.WasCanceled() { 52 return 53 } 54 55 for app := range thisMap { 56 thisMap[app] = new(types.Transaction) 57 } 58 59 iterFunc := func(app types.Appearance, value *types.Transaction) error { 60 bn := base.Blknum(app.BlockNumber) 61 if traces, err := opts.Conn.GetTracesByBlockNumber(bn); err != nil { 62 errMutex.Lock() 63 defer errMutex.Unlock() 64 delete(thisMap, app) 65 return fmt.Errorf("block at %d returned an error: %w", bn, err) 66 67 } else if len(traces) == 0 { 68 errMutex.Lock() 69 defer errMutex.Unlock() 70 delete(thisMap, app) 71 return fmt.Errorf("block at %d has no traces", bn) 72 73 } else { 74 l := make([]types.Trace, 0, len(traces)) 75 for index := range traces { 76 if opts.Articulate { 77 if err = abiCache.ArticulateTrace(&traces[index]); err != nil { 78 errorChan <- err // continue even with an error 79 } 80 } 81 l = append(l, traces[index]) 82 } 83 value.Traces = append(value.Traces, l...) 84 bar.Tick() 85 return nil 86 } 87 } 88 89 iterErrorChan := make(chan error) 90 iterCtx, iterCancel := context.WithCancel(context.Background()) 91 defer iterCancel() 92 go utils.IterateOverMap(iterCtx, iterErrorChan, thisMap, iterFunc) 93 for err := range iterErrorChan { 94 if !testMode || nErrors == 0 { 95 errorChan <- err 96 nErrors++ 97 } 98 } 99 100 items := make([]types.Trace, 0, len(thisMap)) 101 for _, tx := range thisMap { 102 items = append(items, tx.Traces...) 103 } 104 sort.Slice(items, func(i, j int) bool { 105 if items[i].BlockNumber == items[j].BlockNumber { 106 if items[i].TransactionIndex == items[j].TransactionIndex { 107 return items[i].TraceIndex < items[j].TraceIndex 108 } 109 return items[i].TransactionIndex < items[j].TransactionIndex 110 } 111 return items[i].BlockNumber < items[j].BlockNumber 112 }) 113 114 for _, item := range items { 115 modelChan <- &item 116 } 117 } 118 bar.Finish(true /* newLine */) 119 } 120 } 121 122 extraOpts := map[string]any{ 123 "articulate": opts.Articulate, 124 } 125 126 return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts)) 127 }