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  }