github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/export/handle_receipts.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) HandleReceipts(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.Receipt, 0, len(thisMap))
   103  						for _, tx := range thisMap {
   104  							if tx.Receipt == nil {
   105  								continue
   106  							}
   107  							filteredLogs := make([]types.Log, 0, len(tx.Receipt.Logs))
   108  							for _, log := range tx.Receipt.Logs {
   109  								if filter.ApplyLogFilter(&log, addrArray) && logFilter.PassesFilter(&log) {
   110  									if opts.Articulate {
   111  										if err := abiCache.ArticulateLog(&log); err != nil {
   112  											errorChan <- fmt.Errorf("error articulating log: %v", err)
   113  										}
   114  									}
   115  									filteredLogs = append(filteredLogs, log)
   116  								}
   117  							}
   118  							tx.Receipt.Logs = filteredLogs
   119  							items = append(items, tx.Receipt)
   120  						}
   121  
   122  						sort.Slice(items, func(i, j int) bool {
   123  							if opts.Reversed {
   124  								i, j = j, i
   125  							}
   126  							if items[i].BlockNumber == items[j].BlockNumber {
   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  }