github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/rpc/namespaces/eth/api_multi.go (about) 1 package eth 2 3 import ( 4 "errors" 5 "fmt" 6 "math/big" 7 8 "github.com/fibonacci-chain/fbc/app/rpc/monitor" 9 rpctypes "github.com/fibonacci-chain/fbc/app/rpc/types" 10 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 11 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 12 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth" 13 authexported "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported" 14 ctypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types" 15 evmtypes "github.com/fibonacci-chain/fbc/x/evm/types" 16 "github.com/fibonacci-chain/fbc/x/evm/watcher" 17 "github.com/fibonacci-chain/fbc/x/token" 18 19 "github.com/ethereum/go-ethereum/common" 20 "github.com/ethereum/go-ethereum/common/hexutil" 21 ethtypes "github.com/ethereum/go-ethereum/core/types" 22 "github.com/spf13/viper" 23 ) 24 25 const ( 26 FlagEnableMultiCall = "rpc.enable-multi-call" 27 ) 28 29 // GetBalanceBatch returns the provided account's balance up to the provided block number. 30 func (api *PublicEthereumAPI) GetBalanceBatch(addresses []string, blockNrOrHash rpctypes.BlockNumberOrHash) (interface{}, error) { 31 if !viper.GetBool(FlagEnableMultiCall) { 32 return nil, errors.New("the method is not allowed") 33 } 34 35 monitor := monitor.GetMonitor("eth_getBalanceBatch", api.logger, api.Metrics).OnBegin() 36 defer monitor.OnEnd("addresses", addresses, "block number", blockNrOrHash) 37 38 blockNum, err := api.backend.ConvertToBlockNumber(blockNrOrHash) 39 if err != nil { 40 return nil, err 41 } 42 clientCtx := api.clientCtx 43 44 useWatchBackend := api.useWatchBackend(blockNum) 45 if !(blockNum == rpctypes.PendingBlockNumber || blockNum == rpctypes.LatestBlockNumber) && !useWatchBackend { 46 clientCtx = api.clientCtx.WithHeight(blockNum.Int64()) 47 } 48 49 type accBalance struct { 50 Type token.AccType `json:"type"` 51 Balance *hexutil.Big `json:"balance"` 52 } 53 balances := make(map[string]accBalance) 54 for _, addr := range addresses { 55 address, err := sdk.AccAddressFromBech32(addr) 56 if err != nil { 57 return nil, fmt.Errorf("addr:%s,err:%s", addr, err) 58 } 59 if acc, err := api.wrappedBackend.MustGetAccount(address); err == nil { 60 balance := acc.GetCoins().AmountOf(sdk.DefaultBondDenom).BigInt() 61 if balance == nil { 62 balances[addr] = accBalance{accountType(acc), (*hexutil.Big)(sdk.ZeroInt().BigInt())} 63 } else { 64 balances[addr] = accBalance{accountType(acc), (*hexutil.Big)(balance)} 65 } 66 continue 67 } 68 69 bs, err := api.clientCtx.Codec.MarshalJSON(auth.NewQueryAccountParams(address)) 70 if err != nil { 71 return nil, err 72 } 73 res, _, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", auth.QuerierRoute, auth.QueryAccount), bs) 74 if err != nil { 75 continue 76 } 77 78 var account authexported.Account 79 if err := api.clientCtx.Codec.UnmarshalJSON(res, &account); err != nil { 80 return nil, err 81 } 82 83 val := account.GetCoins().AmountOf(sdk.DefaultBondDenom).BigInt() 84 accType := accountType(account) 85 if accType == token.UserAccount || accType == token.ContractAccount { 86 api.watcherBackend.CommitAccountToRpcDb(account) 87 if blockNum != rpctypes.PendingBlockNumber { 88 balances[addr] = accBalance{accType, (*hexutil.Big)(val)} 89 continue 90 } 91 92 // update the address balance with the pending transactions value (if applicable) 93 pendingTxs, err := api.backend.UserPendingTransactions(addr, -1) 94 if err != nil { 95 return nil, err 96 } 97 98 for _, tx := range pendingTxs { 99 if tx == nil { 100 continue 101 } 102 103 if tx.From.String() == addr { 104 val = new(big.Int).Sub(val, tx.Value.ToInt()) 105 } 106 if tx.To.String() == addr { 107 val = new(big.Int).Add(val, tx.Value.ToInt()) 108 } 109 } 110 } 111 balances[addr] = accBalance{accType, (*hexutil.Big)(val)} 112 } 113 return balances, nil 114 } 115 116 // MultiCall performs multiple raw contract call. 117 func (api *PublicEthereumAPI) MultiCall(args []rpctypes.CallArgs, blockNr rpctypes.BlockNumber, _ *[]evmtypes.StateOverrides) ([]hexutil.Bytes, error) { 118 if !viper.GetBool(FlagEnableMultiCall) { 119 return nil, errors.New("the method is not allowed") 120 } 121 122 monitor := monitor.GetMonitor("eth_multiCall", api.logger, api.Metrics).OnBegin() 123 defer monitor.OnEnd("args", args, "block number", blockNr) 124 125 blockNrOrHash := rpctypes.BlockNumberOrHashWithNumber(blockNr) 126 rets := make([]hexutil.Bytes, 0, len(args)) 127 for _, arg := range args { 128 ret, err := api.Call(arg, blockNrOrHash, nil) 129 if err != nil { 130 return rets, err 131 } 132 rets = append(rets, ret) 133 } 134 return rets, nil 135 } 136 137 // GetTransactionsByBlock returns some transactions identified by number or hash. 138 func (api *PublicEthereumAPI) GetTransactionsByBlock(blockNrOrHash rpctypes.BlockNumberOrHash, offset, limit hexutil.Uint) ([]*watcher.Transaction, error) { 139 if !viper.GetBool(FlagEnableMultiCall) { 140 return nil, errors.New("the method is not allowed") 141 } 142 143 monitor := monitor.GetMonitor("eth_getTransactionsByBlock", api.logger, api.Metrics).OnBegin() 144 defer monitor.OnEnd("block number", blockNrOrHash, "offset", offset, "limit", limit) 145 146 blockNum, err := api.backend.ConvertToBlockNumber(blockNrOrHash) 147 if err != nil { 148 return nil, err 149 } 150 151 txs, e := api.wrappedBackend.GetTransactionsByBlockNumber(uint64(blockNum), uint64(offset), uint64(limit)) 152 if e == nil && txs != nil { 153 return txs, nil 154 } 155 156 height := blockNum.Int64() 157 switch blockNum { 158 case rpctypes.PendingBlockNumber: 159 // get all the EVM pending txs 160 pendingTxs, err := api.backend.PendingTransactions() 161 if err != nil { 162 return nil, err 163 } 164 switch { 165 case len(pendingTxs) <= int(offset): 166 return nil, nil 167 case len(pendingTxs) < int(offset+limit): 168 return pendingTxs[offset:], nil 169 default: 170 return pendingTxs[offset : offset+limit], nil 171 } 172 case rpctypes.LatestBlockNumber: 173 height, err = api.backend.LatestBlockNumber() 174 if err != nil { 175 return nil, err 176 } 177 } 178 179 resBlock, err := api.backend.Block(&height) 180 if err != nil { 181 return nil, err 182 } 183 for idx := offset; idx < offset+limit && int(idx) < len(resBlock.Block.Txs); idx++ { 184 tx, _ := api.getTransactionByBlockAndIndex(resBlock.Block, idx) 185 if tx != nil { 186 txs = append(txs, tx) 187 } 188 } 189 return txs, nil 190 } 191 192 // GetTransactionReceiptsByBlock returns the transaction receipt identified by block hash or number. 193 func (api *PublicEthereumAPI) GetTransactionReceiptsByBlock(blockNrOrHash rpctypes.BlockNumberOrHash, offset, limit hexutil.Uint) ([]*watcher.TransactionReceipt, error) { 194 if !viper.GetBool(FlagEnableMultiCall) { 195 return nil, errors.New("the method is not allowed") 196 } 197 198 monitor := monitor.GetMonitor("eth_getTransactionReceiptsByBlock", api.logger, api.Metrics).OnBegin() 199 defer monitor.OnEnd("block number", blockNrOrHash, "offset", offset, "limit", limit) 200 201 txs, err := api.GetTransactionsByBlock(blockNrOrHash, offset, limit) 202 if err != nil || len(txs) == 0 { 203 return nil, err 204 } 205 206 var receipts []*watcher.TransactionReceipt 207 var block *ctypes.ResultBlock 208 var blockHash common.Hash 209 for _, tx := range txs { 210 res, _ := api.wrappedBackend.GetTransactionReceipt(tx.Hash) 211 if res != nil { 212 receipts = append(receipts, res) 213 continue 214 } 215 216 tx, err := api.clientCtx.Client.Tx(tx.Hash.Bytes(), false) 217 if err != nil { 218 // Return nil for transaction when not found 219 return nil, nil 220 } 221 222 if block == nil { 223 // Query block for consensus hash 224 block, err = api.backend.Block(&tx.Height) 225 if err != nil { 226 return nil, err 227 } 228 blockHash = common.BytesToHash(block.Block.Hash()) 229 } 230 231 // Convert tx bytes to eth transaction 232 ethTx, err := rpctypes.RawTxToEthTx(api.clientCtx, tx.Tx, tx.Height) 233 if err != nil { 234 return nil, err 235 } 236 237 err = ethTx.VerifySig(ethTx.ChainID(), tx.Height) 238 if err != nil { 239 return nil, err 240 } 241 242 // Set status codes based on tx result 243 var status = hexutil.Uint64(0) 244 if tx.TxResult.IsOK() { 245 status = hexutil.Uint64(1) 246 } 247 248 txData := tx.TxResult.GetData() 249 data, err := evmtypes.DecodeResultData(txData) 250 if err != nil { 251 status = 0 // transaction failed 252 } 253 254 if len(data.Logs) == 0 || status == 0 { 255 data.Logs = []*ethtypes.Log{} 256 data.Bloom = ethtypes.BytesToBloom(make([]byte, 256)) 257 } 258 contractAddr := &data.ContractAddress 259 if data.ContractAddress == common.HexToAddress("0x00000000000000000000") { 260 contractAddr = nil 261 } 262 263 // fix gasUsed when deliverTx ante handler check sequence invalid 264 gasUsed := tx.TxResult.GasUsed 265 if tx.TxResult.Code == sdkerrors.ErrInvalidSequence.ABCICode() { 266 gasUsed = 0 267 } 268 269 receipt := &watcher.TransactionReceipt{ 270 Status: status, 271 //CumulativeGasUsed: hexutil.Uint64(cumulativeGasUsed), 272 LogsBloom: data.Bloom, 273 Logs: data.Logs, 274 TransactionHash: common.BytesToHash(tx.Hash.Bytes()).String(), 275 ContractAddress: contractAddr, 276 GasUsed: hexutil.Uint64(gasUsed), 277 BlockHash: blockHash.String(), 278 BlockNumber: hexutil.Uint64(tx.Height), 279 TransactionIndex: hexutil.Uint64(tx.Index), 280 From: ethTx.GetFrom(), 281 To: ethTx.To(), 282 } 283 receipts = append(receipts, receipt) 284 } 285 286 return receipts, nil 287 } 288 289 // GetTransactionReceiptsByBlock returns the transaction receipt identified by block hash or number. 290 func (api *PublicEthereumAPI) GetAllTransactionResultsByBlock(blockNrOrHash rpctypes.BlockNumberOrHash, offset, limit hexutil.Uint) ([]*watcher.TransactionResult, error) { 291 if !viper.GetBool(FlagEnableMultiCall) { 292 return nil, errors.New("the method is not allowed") 293 } 294 295 monitor := monitor.GetMonitor("eth_getAllTransactionResultsByBlock", api.logger, api.Metrics).OnBegin() 296 defer monitor.OnEnd("block number", blockNrOrHash, "offset", offset, "limit", limit) 297 298 var results []*watcher.TransactionResult 299 300 blockNum, err := api.backend.ConvertToBlockNumber(blockNrOrHash) 301 if err != nil { 302 return nil, err 303 } 304 305 height := blockNum.Int64() 306 if blockNum == rpctypes.LatestBlockNumber { 307 height, err = api.backend.LatestBlockNumber() 308 if err != nil { 309 return nil, err 310 } 311 } 312 313 // try to get from watch db 314 results, err = api.wrappedBackend.GetTxResultByBlock(api.clientCtx, uint64(height), uint64(offset), uint64(limit)) 315 if err == nil { 316 return results, nil 317 } 318 319 // try to get from node 320 resBlock, err := api.backend.Block(&height) 321 if err != nil { 322 return nil, err 323 } 324 blockHash := common.BytesToHash(resBlock.Block.Hash()) 325 for idx := offset; idx < offset+limit && int(idx) < len(resBlock.Block.Txs); idx++ { 326 realTx, err := rpctypes.RawTxToRealTx(api.clientCtx, resBlock.Block.Txs[idx], 327 blockHash, uint64(resBlock.Block.Height), uint64(idx)) 328 if err != nil { 329 return nil, err 330 } 331 332 if realTx != nil { 333 txHash := resBlock.Block.Txs[idx].Hash(resBlock.Block.Height) 334 queryTx, err := api.clientCtx.Client.Tx(txHash, false) 335 if err != nil { 336 // Return nil for transaction when not found 337 return nil, err 338 } 339 340 var res *watcher.TransactionResult 341 switch realTx.GetType() { 342 case sdk.EvmTxType: 343 res, err = rpctypes.RawTxResultToEthReceipt(api.chainIDEpoch, queryTx, realTx, blockHash) 344 case sdk.StdTxType: 345 res, err = watcher.RawTxResultToStdResponse(api.clientCtx, queryTx, realTx, resBlock.Block.Time) 346 } 347 348 if err != nil { 349 // Return nil for transaction when not found 350 return nil, err 351 } 352 353 results = append(results, res) 354 } 355 } 356 357 return results, nil 358 }