github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/state/handle_call.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 statePkg
     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/call"
    15  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers"
    16  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    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 *StateOptions) HandleCall(rCtx *output.RenderCtx) error {
    23  	chain := opts.Globals.Chain
    24  	testMode := opts.Globals.TestMode
    25  	nErrors := 0
    26  
    27  	artFunc := func(str string, function *types.Function) error {
    28  		return articulate.ArticulateFunction(function, "", str[2:])
    29  	}
    30  
    31  	callAddress := opts.GetCallAddress()
    32  	fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
    33  		apps, _, err := identifiers.IdsToApps(chain, opts.BlockIds)
    34  		if err != nil {
    35  			errorChan <- err
    36  			rCtx.Cancel()
    37  		}
    38  
    39  		if sliceOfMaps, cnt, err := types.AsSliceOfMaps[[]types.Result](apps, false); err != nil {
    40  			errorChan <- err
    41  			rCtx.Cancel()
    42  
    43  		} else if cnt == 0 {
    44  			errorChan <- fmt.Errorf("no blocks found for the query")
    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.Result)
    61  				}
    62  
    63  				iterFunc := func(app types.Appearance, value *[]types.Result) error {
    64  					bn := base.Blknum(app.BlockNumber)
    65  					for _, c := range opts.Calls {
    66  						if contractCall, _, err := call.NewContractCall(opts.Conn, callAddress, c); err != nil {
    67  							delete(thisMap, app)
    68  							return fmt.Errorf("the --call value provided (%s) was not found: %s", c, err)
    69  
    70  						} else {
    71  							contractCall.BlockNumber = bn
    72  							results, err := contractCall.Call(artFunc)
    73  							if err != nil {
    74  								delete(thisMap, app)
    75  								return err
    76  							} else {
    77  								bar.Tick()
    78  								*value = append(*value, *results)
    79  							}
    80  						}
    81  					}
    82  					return nil
    83  				}
    84  
    85  				iterErrorChan := make(chan error)
    86  				iterCtx, iterCancel := context.WithCancel(context.Background())
    87  				defer iterCancel()
    88  				go utils.IterateOverMap(iterCtx, iterErrorChan, thisMap, iterFunc)
    89  				for err := range iterErrorChan {
    90  					if !testMode || nErrors == 0 {
    91  						errorChan <- err
    92  						nErrors++
    93  					}
    94  				}
    95  
    96  				items := make([]types.Result, 0, len(thisMap))
    97  				for _, v := range thisMap {
    98  					items = append(items, *v...)
    99  				}
   100  
   101  				sort.Slice(items, func(i, j int) bool {
   102  					return items[i].BlockNumber < items[j].BlockNumber
   103  				})
   104  
   105  				for _, item := range items {
   106  					modelChan <- &item
   107  				}
   108  			}
   109  			bar.Finish(true /* newLine */)
   110  		}
   111  	}
   112  
   113  	extraOpts := map[string]any{
   114  		"articulate": opts.Articulate,
   115  	}
   116  
   117  	return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts))
   118  }
   119  
   120  func (opts *StateOptions) GetCallAddress() base.Address {
   121  	// Note that the validator precludes the possibility of having more than one address
   122  	// if the call option is present.
   123  	callAddress := base.HexToAddress(opts.Addrs[0])
   124  	proxy := base.HexToAddress(opts.ProxyFor)
   125  	if !proxy.IsZero() {
   126  		callAddress = proxy
   127  	}
   128  	return callAddress
   129  }