github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/export/handle_statements.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/base" 13 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/filter" 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/ledger" 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/types" 19 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" 20 ) 21 22 func (opts *ExportOptions) HandleStatements(rCtx *output.RenderCtx, monitorArray []monitor.Monitor) error { 23 chain := opts.Globals.Chain 24 testMode := opts.Globals.TestMode 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 fetchData := func(modelChan chan types.Modeler, errorChan chan error) { 34 for _, mon := range monitorArray { 35 if apps, cnt, err := mon.ReadAndFilterAppearances(filter, false /* withCount */); 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 continue 42 43 } else { 44 if sliceOfMaps, _, err := types.AsSliceOfMaps[types.Transaction](apps, filter.Reversed); err != nil { 45 errorChan <- err 46 rCtx.Cancel() 47 48 } else { 49 showProgress := opts.Globals.ShowProgress() 50 bar := logger.NewBar(logger.BarOptions{ 51 Prefix: mon.Address.Hex(), 52 Enabled: showProgress, 53 Total: int64(cnt), 54 }) 55 56 // TODO: BOGUS - THIS IS NOT CONCURRENCY SAFE 57 finished := false 58 for _, thisMap := range sliceOfMaps { 59 if rCtx.WasCanceled() { 60 return 61 } 62 63 if finished { 64 continue 65 } 66 67 for app := range thisMap { 68 thisMap[app] = new(types.Transaction) 69 } 70 71 iterFunc := func(app types.Appearance, value *types.Transaction) error { 72 if tx, err := opts.Conn.GetTransactionByAppearance(&app, false); err != nil { 73 return err 74 } else { 75 passes, _ := filter.ApplyTxFilters(tx) 76 if passes { 77 *value = *tx 78 } 79 if bar != nil { 80 bar.Tick() 81 } 82 return nil 83 } 84 } 85 86 // Set up and interate over the map calling iterFunc for each appearance 87 iterCtx, iterCancel := context.WithCancel(context.Background()) 88 defer iterCancel() 89 errChan := make(chan error) 90 go utils.IterateOverMap(iterCtx, errChan, thisMap, iterFunc) 91 if stepErr := <-errChan; stepErr != nil { 92 errorChan <- stepErr 93 return 94 } 95 96 txArray := make([]*types.Transaction, 0, len(thisMap)) 97 for _, tx := range thisMap { 98 txArray = append(txArray, tx) 99 } 100 101 sort.Slice(txArray, func(i, j int) bool { 102 if txArray[i].BlockNumber == txArray[j].BlockNumber { 103 return txArray[i].TransactionIndex < txArray[j].TransactionIndex 104 } 105 return txArray[i].BlockNumber < txArray[j].BlockNumber 106 }) 107 108 apps := make([]types.Appearance, 0, len(thisMap)) 109 for _, tx := range txArray { 110 apps = append(apps, types.Appearance{ 111 BlockNumber: uint32(tx.BlockNumber), 112 TransactionIndex: uint32(tx.TransactionIndex), 113 }) 114 } 115 116 ledgers := ledger.NewLedger( 117 opts.Conn, 118 mon.Address, 119 opts.FirstBlock, 120 opts.LastBlock, 121 opts.Globals.Ether, 122 testMode, 123 opts.NoZero, 124 opts.Traces, 125 opts.Reversed, 126 &opts.Asset, 127 ) 128 _ = ledgers.SetContexts(chain, apps) 129 130 items := make([]types.Statement, 0, len(thisMap)) 131 for _, tx := range txArray { 132 if statements, err := ledgers.GetStatements(opts.Conn, filter, tx); err != nil { 133 errorChan <- err 134 135 } else if len(statements) > 0 { 136 items = append(items, statements...) 137 } 138 } 139 140 sort.Slice(items, func(i, j int) bool { 141 if opts.Reversed { 142 i, j = j, i 143 } 144 if items[i].BlockNumber == items[j].BlockNumber { 145 if items[i].TransactionIndex == items[j].TransactionIndex { 146 return items[i].LogIndex < items[j].LogIndex 147 } 148 return items[i].TransactionIndex < items[j].TransactionIndex 149 } 150 return items[i].BlockNumber < items[j].BlockNumber 151 }) 152 153 for _, item := range items { 154 var passes bool 155 passes, finished = filter.ApplyCountFilter() 156 if passes { 157 modelChan <- &item 158 } 159 if finished { 160 break 161 } 162 } 163 } 164 bar.Finish(true /* newLine */) 165 } 166 } 167 } 168 } 169 170 extraOpts := map[string]any{ 171 "articulate": opts.Articulate, 172 "export": true, 173 } 174 175 return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts)) 176 }