github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/export/handle_logs.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 exportPkg 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/filter" 15 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/monitor" 17 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output" 18 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 19 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 20 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" 21 ) 22 23 func (opts *ExportOptions) HandleLogs(rCtx *output.RenderCtx, monitorArray []monitor.Monitor) error { 24 abiCache := articulate.NewAbiCache(opts.Conn, opts.Articulate) 25 filter := filter.NewFilter( 26 opts.Reversed, 27 opts.Reverted, 28 opts.Fourbytes, 29 base.BlockRange{First: opts.FirstBlock, Last: opts.LastBlock}, 30 base.RecordRange{First: opts.FirstRecord, Last: opts.GetMax()}, 31 ) 32 33 addrArray := make([]base.Address, 0, len(monitorArray)) 34 for _, mon := range monitorArray { 35 addrArray = append(addrArray, mon.Address) 36 } 37 logFilter := rpc.NewLogFilter(opts.Emitter, opts.Topic) 38 39 fetchData := func(modelChan chan types.Modeler, errorChan chan error) { 40 for _, mon := range monitorArray { 41 if apps, cnt, err := mon.ReadAndFilterAppearances(filter, false /* withCount */); err != nil { 42 errorChan <- err 43 rCtx.Cancel() 44 45 } else if cnt == 0 { 46 errorChan <- fmt.Errorf("no blocks found for the query") 47 continue 48 49 } else { 50 if sliceOfMaps, _, err := types.AsSliceOfMaps[types.Transaction](apps, filter.Reversed); err != nil { 51 errorChan <- err 52 rCtx.Cancel() 53 54 } else { 55 showProgress := opts.Globals.ShowProgress() 56 bar := logger.NewBar(logger.BarOptions{ 57 Prefix: mon.Address.Hex(), 58 Enabled: showProgress, 59 Total: int64(cnt), 60 }) 61 62 // TODO: BOGUS - THIS IS NOT CONCURRENCY SAFE 63 finished := false 64 for _, thisMap := range sliceOfMaps { 65 if rCtx.WasCanceled() { 66 return 67 } 68 69 if finished { 70 continue 71 } 72 73 for app := range thisMap { 74 thisMap[app] = new(types.Transaction) 75 } 76 77 iterFunc := func(app types.Appearance, value *types.Transaction) error { 78 if tx, err := opts.Conn.GetTransactionByAppearance(&app, false); err != nil { 79 return err 80 } else { 81 passes, _ := filter.ApplyTxFilters(tx) 82 if passes { 83 *value = *tx 84 } 85 if bar != nil { 86 bar.Tick() 87 } 88 return nil 89 } 90 } 91 92 // Set up and interate over the map calling iterFunc for each appearance 93 iterCtx, iterCancel := context.WithCancel(context.Background()) 94 defer iterCancel() 95 errChan := make(chan error) 96 go utils.IterateOverMap(iterCtx, errChan, thisMap, iterFunc) 97 if stepErr := <-errChan; stepErr != nil { 98 errorChan <- stepErr 99 return 100 } 101 102 items := make([]*types.Log, 0, len(thisMap)) 103 for _, tx := range thisMap { 104 if tx.Receipt == nil { 105 continue 106 } 107 for _, log := range tx.Receipt.Logs { 108 if filter.ApplyLogFilter(&log, addrArray) && logFilter.PassesFilter(&log) { 109 if opts.Articulate { 110 if err = abiCache.ArticulateLog(&log); err != nil { 111 errorChan <- fmt.Errorf("error articulating log: %v", err) 112 } 113 } 114 items = append(items, &log) 115 } 116 } 117 } 118 119 sort.Slice(items, func(i, j int) bool { 120 if opts.Reversed { 121 i, j = j, i 122 } 123 if items[i].BlockNumber == items[j].BlockNumber { 124 if items[i].TransactionIndex == items[j].TransactionIndex { 125 return items[i].LogIndex < items[j].LogIndex 126 } 127 return items[i].TransactionIndex < items[j].TransactionIndex 128 } 129 return items[i].BlockNumber < items[j].BlockNumber 130 }) 131 132 for _, item := range items { 133 var passes bool 134 passes, finished = filter.ApplyCountFilter() 135 if passes { 136 modelChan <- item 137 } 138 if finished { 139 break 140 } 141 } 142 } 143 bar.Finish(true /* newLine */) 144 } 145 } 146 } 147 } 148 149 extraOpts := map[string]any{ 150 "articulate": opts.Articulate, 151 "export": true, 152 } 153 154 return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts)) 155 }