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