github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/transactions/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 transactionsPkg 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/identifiers" 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 15 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output" 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 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 *TransactionsOptions) HandleLogs(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 logFilter := rpc.NewLogFilter(opts.Emitter, opts.Topic) 28 29 fetchData := func(modelChan chan types.Modeler, errorChan chan error) { 30 apps, _, err := identifiers.IdsToApps(chain, opts.TransactionIds) 31 if err != nil { 32 errorChan <- err 33 rCtx.Cancel() 34 } 35 36 if sliceOfMaps, cnt, err := types.AsSliceOfMaps[types.Transaction](apps, false); err != nil { 37 errorChan <- err 38 rCtx.Cancel() 39 40 } else if cnt == 0 { 41 errorChan <- fmt.Errorf("no transactions found") 42 rCtx.Cancel() 43 44 } else { 45 showProgress := opts.Globals.ShowProgress() 46 bar := logger.NewBar(logger.BarOptions{ 47 Enabled: showProgress, 48 Total: int64(cnt), 49 }) 50 51 for _, thisMap := range sliceOfMaps { 52 if rCtx.WasCanceled() { 53 return 54 } 55 56 for app := range thisMap { 57 thisMap[app] = new(types.Transaction) 58 } 59 60 iterFunc := func(app types.Appearance, value *types.Transaction) error { 61 if tx, err := opts.Conn.GetTransactionByAppearance(&app, opts.Traces /* needsTraces */); err != nil { 62 delete(thisMap, app) 63 return fmt.Errorf("transaction at %s returned an error: %w", app.Orig(), err) 64 65 } else if tx == nil { 66 delete(thisMap, app) 67 return fmt.Errorf("transaction at %s has no logs", app.Orig()) 68 69 } else { 70 if opts.Articulate && tx.ArticulatedTx == nil { 71 if err = abiCache.ArticulateTransaction(tx); err != nil { 72 errorChan <- err // continue even with an error 73 } 74 } 75 *value = *tx 76 bar.Tick() 77 return nil 78 } 79 } 80 81 iterErrorChan := make(chan error) 82 iterCtx, iterCancel := context.WithCancel(context.Background()) 83 defer iterCancel() 84 go utils.IterateOverMap(iterCtx, iterErrorChan, thisMap, iterFunc) 85 for err := range iterErrorChan { 86 if !testMode || nErrors == 0 { 87 errorChan <- err 88 nErrors++ 89 } 90 } 91 92 items := make([]types.Transaction, 0, len(thisMap)) 93 for _, tx := range thisMap { 94 items = append(items, *tx) 95 } 96 sort.Slice(items, func(i, j int) bool { 97 if items[i].BlockNumber == items[j].BlockNumber { 98 return items[i].TransactionIndex < items[j].TransactionIndex 99 } 100 return items[i].BlockNumber < items[j].BlockNumber 101 }) 102 103 for _, item := range items { 104 for _, log := range item.Receipt.Logs { 105 if logFilter.PassesFilter(&log) { 106 modelChan <- &log 107 } 108 } 109 } 110 } 111 bar.Finish(true /* newLine */) 112 } 113 } 114 115 extraOpts := map[string]any{ 116 "logs": opts.Logs, 117 "traces": opts.Traces, 118 "addresses": opts.Uniq, 119 } 120 121 return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts)) 122 }