github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/rpc/provider/key.go (about)

     1  package provider
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  
     8  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
     9  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config"
    10  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc"
    11  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc/query"
    12  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    13  	"golang.org/x/time/rate"
    14  )
    15  
    16  const keyFirstPage = "latest"
    17  const keyRequestsPerSecond = 20
    18  const keyMaxPerPage = 1000
    19  
    20  func keyPrepareQuery(q *Query) (result *Query) {
    21  	result = q.Dup()
    22  	result.Resources = []string{"key"}
    23  	return
    24  }
    25  
    26  type KeyProvider struct {
    27  	printProgress bool
    28  	perPage       int
    29  	conn          *rpc.Connection
    30  	limiter       *rate.Limiter
    31  	baseUrl       string
    32  	chain         string
    33  }
    34  
    35  func NewKeyProvider(conn *rpc.Connection, chain string) (p *KeyProvider, err error) {
    36  	keyEndpoint := config.GetChain(chain).KeyEndpoint
    37  	if keyEndpoint == "" {
    38  		err = errors.New("missing Key endpoint URL")
    39  		return
    40  	}
    41  
    42  	p = &KeyProvider{
    43  		conn:    conn,
    44  		chain:   chain,
    45  		perPage: keyMaxPerPage,
    46  		baseUrl: keyEndpoint,
    47  	}
    48  	p.printProgress = true
    49  	p.limiter = rate.NewLimiter(keyRequestsPerSecond, keyRequestsPerSecond)
    50  
    51  	return
    52  }
    53  
    54  func (p *KeyProvider) PrintProgress() bool {
    55  	return p.printProgress
    56  }
    57  
    58  func (p *KeyProvider) SetPrintProgress(print bool) {
    59  	p.printProgress = print
    60  }
    61  
    62  func (p *KeyProvider) NewPaginator(query *Query) Paginator {
    63  	pageId := query.StartPageId
    64  	if pageId == "" {
    65  		pageId = keyFirstPage
    66  	}
    67  	perPageValue := query.PerPage
    68  	if perPageValue == 0 {
    69  		perPageValue = keyMaxPerPage
    70  	}
    71  
    72  	return NewPageIdPaginator(pageId, pageId, int(perPageValue))
    73  }
    74  
    75  func (p *KeyProvider) TransactionsByAddress(ctx context.Context, query *Query, errorChan chan error) (txChan chan types.Slurp) {
    76  	txChan = make(chan types.Slurp, providerChannelBufferSize)
    77  
    78  	// Key supports only 1 resource
    79  	prepQuery := keyPrepareQuery(query)
    80  	slurpedChan := fetchAndFilterData(ctx, p, prepQuery, errorChan, p.fetchData)
    81  	go func() {
    82  		defer close(txChan)
    83  		for {
    84  			select {
    85  			case <-ctx.Done():
    86  				return
    87  			case item, ok := <-slurpedChan:
    88  				if !ok {
    89  					return
    90  				}
    91  				tx, err := p.conn.GetTransactionByAppearance(item.Appearance, false)
    92  				if err != nil {
    93  					errorChan <- err
    94  					continue
    95  				}
    96  				txChan <- *(transactionToSlurp(tx))
    97  			}
    98  		}
    99  	}()
   100  
   101  	return
   102  }
   103  
   104  func transactionToSlurp(tx *types.Transaction) *types.Slurp {
   105  	return &types.Slurp{
   106  		// ArticulatedTx:     tx.ArticulatedTx,
   107  		BlockHash:   tx.BlockHash,
   108  		BlockNumber: tx.BlockNumber,
   109  		// ContractAddress:   tx.ContractAddress,
   110  		// CumulativeGasUsed: tx.CumulativeGasUsed,
   111  		// Ether:             tx.Ether,
   112  		From: tx.From,
   113  		// FunctionName:      tx.FunctionName,
   114  		Gas:      tx.Gas,
   115  		GasPrice: tx.GasPrice,
   116  		GasUsed:  tx.GasUsed,
   117  		HasToken: tx.HasToken,
   118  		Hash:     tx.Hash,
   119  		Input:    tx.Input,
   120  		IsError:  tx.IsError,
   121  		// MethodId:          tx.MethodId,
   122  		Nonce:            tx.Nonce,
   123  		Timestamp:        tx.Timestamp,
   124  		To:               tx.To,
   125  		TransactionIndex: tx.TransactionIndex,
   126  		// TxReceiptStatus:   tx.TxReceiptStatus,
   127  		// ValidatorIndex:    tx.ValidatorIndex,
   128  		Value: tx.Value,
   129  		// WithdrawalIndex:   tx.WithdrawalIndex,
   130  	}
   131  }
   132  
   133  func (p *KeyProvider) Appearances(ctx context.Context, query *Query, errorChan chan error) (appChan chan types.Appearance) {
   134  	appChan = make(chan types.Appearance, providerChannelBufferSize)
   135  
   136  	// Key supports only 1 resource
   137  	prepQuery := keyPrepareQuery(query)
   138  	slurpedChan := fetchAndFilterData(ctx, p, prepQuery, errorChan, p.fetchData)
   139  	go func() {
   140  		defer close(appChan)
   141  		for {
   142  			select {
   143  			case <-ctx.Done():
   144  				return
   145  			case item, ok := <-slurpedChan:
   146  				if !ok {
   147  					return
   148  				}
   149  				appChan <- *item.Appearance
   150  			}
   151  		}
   152  	}()
   153  
   154  	return
   155  }
   156  
   157  func (p *KeyProvider) Count(ctx context.Context, query *Query, errorChan chan error) (monitorChan chan types.Monitor) {
   158  	// Key supports only 1 resource
   159  	prepQuery := keyPrepareQuery(query)
   160  	slurpedChan := fetchAndFilterData(ctx, p, prepQuery, errorChan, p.fetchData)
   161  	return countSlurped(ctx, query, slurpedChan)
   162  }
   163  
   164  type keyAppearancesRequestParam struct {
   165  	Address string `json:"address"`
   166  	PageId  string `json:"pageId"`
   167  	PerPage int    `json:"perPage"`
   168  }
   169  
   170  type keyResponseBody struct {
   171  	Data []KeyAppearance `json:"data"`
   172  	Meta Meta            `json:"meta"`
   173  }
   174  
   175  type KeyAppearance struct {
   176  	BlockNumber      string `json:"blockNumber"`
   177  	TransactionIndex string `json:"transactionIndex"`
   178  }
   179  
   180  func (k *KeyAppearance) Slurp() (s types.Slurp, err error) {
   181  	s = types.Slurp{}
   182  	if err = json.Unmarshal([]byte(k.BlockNumber), &s.BlockNumber); err != nil {
   183  		return
   184  	}
   185  	err = json.Unmarshal([]byte(k.TransactionIndex), &s.TransactionIndex)
   186  	return
   187  }
   188  
   189  func (k *KeyAppearance) Appearance(address base.Address) (a types.Appearance, err error) {
   190  	a = types.Appearance{
   191  		Address: address,
   192  	}
   193  	if err = json.Unmarshal([]byte(k.BlockNumber), &a.BlockNumber); err != nil {
   194  		return
   195  	}
   196  	err = json.Unmarshal([]byte(k.TransactionIndex), &a.TransactionIndex)
   197  	return
   198  }
   199  
   200  type Meta struct {
   201  	LastIndexedBlock base.Blknum `json:"lastIndexedBlock,omitempty"`
   202  	Address          string      `json:"address,omitempty"`
   203  	PreviousPageId   string      `json:"previousPageId"`
   204  	NextPageId       string      `json:"nextPageId"`
   205  }
   206  
   207  func (e *KeyProvider) fetchData(ctx context.Context, address base.Address, paginator Paginator, _ string) (data []SlurpedPageItem, count int, err error) {
   208  	pageId, ok := paginator.Page().(string)
   209  	if !ok {
   210  		err = errors.New("cannot get page id")
   211  		return
   212  	}
   213  
   214  	method := "tb_getAppearances"
   215  	params := query.Params{keyAppearancesRequestParam{
   216  		Address: address.Hex(),
   217  		PageId:  pageId,
   218  		PerPage: paginator.PerPage(),
   219  	}}
   220  
   221  	var response *keyResponseBody
   222  	if response, err = query.QueryUrl[keyResponseBody](e.baseUrl, method, params); err != nil {
   223  		return
   224  	}
   225  
   226  	data = make([]SlurpedPageItem, 0, len(response.Data))
   227  	for _, keyAppearance := range response.Data {
   228  		appearance, err := keyAppearance.Appearance(base.HexToAddress(response.Meta.Address))
   229  		if err != nil {
   230  			return []SlurpedPageItem{}, 0, err
   231  		}
   232  		data = append(data, SlurpedPageItem{
   233  			Appearance: &appearance,
   234  		})
   235  	}
   236  	// update paginator
   237  	_ = paginator.SetNextPage(response.Meta.PreviousPageId)
   238  	paginator.SetDone(response.Meta.PreviousPageId == "")
   239  
   240  	count = len(data)
   241  	return
   242  }