github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/abis/handle_find.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 abisPkg
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"encoding/hex"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"runtime"
    15  	"sort"
    16  	"strings"
    17  	"sync"
    18  
    19  	"github.com/ethereum/go-ethereum/crypto"
    20  	ants "github.com/panjf2000/ants/v2"
    21  
    22  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config"
    23  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    24  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output"
    25  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/progress"
    26  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    27  )
    28  
    29  func (opts *AbisOptions) HandleFind(rCtx *output.RenderCtx) error {
    30  	testMode := opts.Globals.TestMode
    31  	/* wanted */ /* freq */ /* max */
    32  	scanBar := progress.NewScanBar(uint64(len(opts.Find)), 13919, 50000000, .5)
    33  
    34  	// TODO: we might want to use utils.IterateOver Map here
    35  
    36  	fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
    37  		var results []types.Function
    38  		var wg sync.WaitGroup
    39  		mutex := sync.Mutex{}
    40  		checkOne, _ := ants.NewPoolWithFunc(runtime.NumCPU()*2, func(testSig any) {
    41  			defer wg.Done()
    42  			byts := []byte(testSig.(string))
    43  			sigBytes := crypto.Keccak256(byts)
    44  			for _, arg := range opts.Find {
    45  				if !opts.Globals.TestMode {
    46  					scanBar.Report(os.Stderr, "Scanning", testSig.(string))
    47  				}
    48  				str, _ := hex.DecodeString(arg[2:])
    49  				if bytes.Equal(sigBytes[:len(str)], str) {
    50  					scanBar.Found++
    51  					logger.Progress(len(opts.Find) < 2, "Found", scanBar.Found, "of", scanBar.Wanted, arg, testSig)
    52  					found := types.Function{
    53  						Encoding:  arg,
    54  						Signature: testSig.(string),
    55  					}
    56  					if testMode {
    57  						mutex.Lock()
    58  						results = append(results, found)
    59  						mutex.Unlock()
    60  					} else {
    61  						modelChan <- &found
    62  					}
    63  					return
    64  				}
    65  			}
    66  		})
    67  		defer checkOne.Release()
    68  
    69  		// TODO: UnchainedIndex --> This could be part of unchained index
    70  		sigFile := filepath.Join(config.PathToRootConfig(), "abis", "known-000", "uniq_sigs.tab")
    71  		sigsFile, err := os.OpenFile(sigFile, os.O_RDONLY, 0)
    72  		if err != nil {
    73  			errorChan <- err
    74  			rCtx.Cancel()
    75  			return
    76  		}
    77  		defer func() {
    78  			sigsFile.Close()
    79  		}()
    80  		_, _ = sigsFile.Seek(0, io.SeekStart)
    81  		sigsScanner := bufio.NewScanner(sigsFile)
    82  		sigsScanner.Split(bufio.ScanLines)
    83  
    84  		// TODO: UnchainedIndex --> This could be part of unchained index
    85  		funcFile := filepath.Join(config.PathToRootConfig(), "abis", "known-000", "uniq_funcs.tab")
    86  		funcsFile, _ := os.OpenFile(funcFile, os.O_RDONLY, 0)
    87  		defer func() {
    88  			funcsFile.Close()
    89  		}()
    90  
    91  		for sigsScanner.Scan() {
    92  			s := sigsScanner.Text()
    93  			hitsSignature := opts.hitsHint(s)
    94  
    95  			_, _ = funcsFile.Seek(0, io.SeekStart)
    96  			funcsScanner := bufio.NewScanner(funcsFile)
    97  			funcsScanner.Split(bufio.ScanLines)
    98  
    99  			for funcsScanner.Scan() {
   100  				f := funcsScanner.Text()
   101  				hitsFunction := opts.hitsHint(f)
   102  				if hitsSignature || hitsFunction {
   103  					wg.Add(1)
   104  					call := f + "(" + s + ")"
   105  					_ = checkOne.Invoke(call)
   106  				}
   107  			}
   108  
   109  			if scanBar.Satisfied() && !opts.Globals.Verbose {
   110  				break
   111  			}
   112  		}
   113  
   114  		defer wg.Wait()
   115  
   116  		if opts.Globals.TestMode {
   117  			// Otherwise the test is not reproducable
   118  			sort.Slice(results, func(i, j int) bool {
   119  				return results[i].Signature < results[j].Signature
   120  			})
   121  			for _, item := range results {
   122  				modelChan <- &item
   123  			}
   124  		}
   125  	}
   126  
   127  	extraOpts := map[string]any{
   128  		"encodingSignatureOnly": true,
   129  	}
   130  	return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts))
   131  }
   132  
   133  func (opts *AbisOptions) hitsHint(test string) bool {
   134  	hits := len(opts.Hint) == 0
   135  	if !hits {
   136  		// test = strings.ToLower(test)
   137  		for _, hint := range opts.Hint {
   138  			if strings.Contains(test, hint) {
   139  				return true
   140  			}
   141  		}
   142  	}
   143  	return hits
   144  }