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 }