laconicd-deprecated/rpc/backend/utils.go
mergify[bot] 949674e511
fix(rpc): align fee history (backport #1611) (#1620)
* fix(rpc): align fee history (#1611)

* update nix

* add next fee in fee history

* fix test

* add change doc

* height + 1 for next fee

* cross check baseFeePerGas len

* Update tests/integration_tests/test_fee_history.py

Co-authored-by: MalteHerrmann <42640438+MalteHerrmann@users.noreply.github.com>

* fix oldestBlock & align earliest input as eth

* update doc

* update nix

* isort test_fee_history.py

* fix test

* align rpc res as eth

* add cross check

* add baseFeePerGas len check

* add oldestBlock check

Co-authored-by: MalteHerrmann <42640438+MalteHerrmann@users.noreply.github.com>
Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
(cherry picked from commit 47fdfd3d8318358c2578b610bc35bc8c1ebb131e)

# Conflicts:
#	CHANGELOG.md

* address merge conflicts

Co-authored-by: mmsqe <mavis@crypto.com>
Co-authored-by: MalteHerrmann <malte@evmos.org>
Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
2023-01-20 16:49:30 +01:00

313 lines
8.9 KiB
Go

// Copyright 2021 Evmos Foundation
// This file is part of Evmos' Ethermint library.
//
// The Ethermint library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The Ethermint library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE
package backend
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"sort"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc"
ethtypes "github.com/ethereum/go-ethereum/core/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/evmos/ethermint/rpc/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"github.com/tendermint/tendermint/proto/tendermint/crypto"
)
type txGasAndReward struct {
gasUsed uint64
reward *big.Int
}
type sortGasAndReward []txGasAndReward
func (s sortGasAndReward) Len() int { return len(s) }
func (s sortGasAndReward) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s sortGasAndReward) Less(i, j int) bool {
return s[i].reward.Cmp(s[j].reward) < 0
}
// getAccountNonce returns the account nonce for the given account address.
// If the pending value is true, it will iterate over the mempool (pending)
// txs in order to compute and return the pending tx sequence.
// Todo: include the ability to specify a blockNumber
func (b *Backend) getAccountNonce(accAddr common.Address, pending bool, height int64, logger log.Logger) (uint64, error) {
queryClient := authtypes.NewQueryClient(b.clientCtx)
adr := sdk.AccAddress(accAddr.Bytes()).String()
ctx := types.ContextWithHeight(height)
res, err := queryClient.Account(ctx, &authtypes.QueryAccountRequest{Address: adr})
if err != nil {
st, ok := status.FromError(err)
// treat as account doesn't exist yet
if ok && st.Code() == codes.NotFound {
return 0, nil
}
return 0, err
}
var acc authtypes.AccountI
if err := b.clientCtx.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil {
return 0, err
}
nonce := acc.GetSequence()
if !pending {
return nonce, nil
}
// the account retriever doesn't include the uncommitted transactions on the nonce so we need to
// to manually add them.
pendingTxs, err := b.PendingTransactions()
if err != nil {
logger.Error("failed to fetch pending transactions", "error", err.Error())
return nonce, nil
}
// add the uncommitted txs to the nonce counter
// only supports `MsgEthereumTx` style tx
for _, tx := range pendingTxs {
for _, msg := range (*tx).GetMsgs() {
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
// not ethereum tx
break
}
sender, err := ethMsg.GetSender(b.chainID)
if err != nil {
continue
}
if sender == accAddr {
nonce++
}
}
}
return nonce, nil
}
// output: targetOneFeeHistory
func (b *Backend) processBlock(
tendermintBlock *tmrpctypes.ResultBlock,
ethBlock *map[string]interface{},
rewardPercentiles []float64,
tendermintBlockResult *tmrpctypes.ResultBlockResults,
targetOneFeeHistory *types.OneFeeHistory,
) error {
blockHeight := tendermintBlock.Block.Height
blockBaseFee, err := b.BaseFee(tendermintBlockResult)
if err != nil {
return err
}
// set basefee
targetOneFeeHistory.BaseFee = blockBaseFee
cfg := b.ChainConfig()
if cfg.IsLondon(big.NewInt(blockHeight + 1)) {
targetOneFeeHistory.NextBaseFee = misc.CalcBaseFee(cfg, b.CurrentHeader())
} else {
targetOneFeeHistory.NextBaseFee = new(big.Int)
}
// set gas used ratio
gasLimitUint64, ok := (*ethBlock)["gasLimit"].(hexutil.Uint64)
if !ok {
return fmt.Errorf("invalid gas limit type: %T", (*ethBlock)["gasLimit"])
}
gasUsedBig, ok := (*ethBlock)["gasUsed"].(*hexutil.Big)
if !ok {
return fmt.Errorf("invalid gas used type: %T", (*ethBlock)["gasUsed"])
}
gasusedfloat, _ := new(big.Float).SetInt(gasUsedBig.ToInt()).Float64()
if gasLimitUint64 <= 0 {
return fmt.Errorf("gasLimit of block height %d should be bigger than 0 , current gaslimit %d", blockHeight, gasLimitUint64)
}
gasUsedRatio := gasusedfloat / float64(gasLimitUint64)
blockGasUsed := gasusedfloat
targetOneFeeHistory.GasUsedRatio = gasUsedRatio
rewardCount := len(rewardPercentiles)
targetOneFeeHistory.Reward = make([]*big.Int, rewardCount)
for i := 0; i < rewardCount; i++ {
targetOneFeeHistory.Reward[i] = big.NewInt(0)
}
// check tendermintTxs
tendermintTxs := tendermintBlock.Block.Txs
tendermintTxResults := tendermintBlockResult.TxsResults
tendermintTxCount := len(tendermintTxs)
var sorter sortGasAndReward
for i := 0; i < tendermintTxCount; i++ {
eachTendermintTx := tendermintTxs[i]
eachTendermintTxResult := tendermintTxResults[i]
tx, err := b.clientCtx.TxConfig.TxDecoder()(eachTendermintTx)
if err != nil {
b.logger.Debug("failed to decode transaction in block", "height", blockHeight, "error", err.Error())
continue
}
txGasUsed := uint64(eachTendermintTxResult.GasUsed)
for _, msg := range tx.GetMsgs() {
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
continue
}
tx := ethMsg.AsTransaction()
reward := tx.EffectiveGasTipValue(blockBaseFee)
if reward == nil {
reward = big.NewInt(0)
}
sorter = append(sorter, txGasAndReward{gasUsed: txGasUsed, reward: reward})
}
}
// return an all zero row if there are no transactions to gather data from
ethTxCount := len(sorter)
if ethTxCount == 0 {
return nil
}
sort.Sort(sorter)
var txIndex int
sumGasUsed := sorter[0].gasUsed
for i, p := range rewardPercentiles {
thresholdGasUsed := uint64(blockGasUsed * p / 100)
for sumGasUsed < thresholdGasUsed && txIndex < ethTxCount-1 {
txIndex++
sumGasUsed += sorter[txIndex].gasUsed
}
targetOneFeeHistory.Reward[i] = sorter[txIndex].reward
}
return nil
}
// AllTxLogsFromEvents parses all ethereum logs from cosmos events
func AllTxLogsFromEvents(events []abci.Event) ([][]*ethtypes.Log, error) {
allLogs := make([][]*ethtypes.Log, 0, 4)
for _, event := range events {
if event.Type != evmtypes.EventTypeTxLog {
continue
}
logs, err := ParseTxLogsFromEvent(event)
if err != nil {
return nil, err
}
allLogs = append(allLogs, logs)
}
return allLogs, nil
}
// TxLogsFromEvents parses ethereum logs from cosmos events for specific msg index
func TxLogsFromEvents(events []abci.Event, msgIndex int) ([]*ethtypes.Log, error) {
for _, event := range events {
if event.Type != evmtypes.EventTypeTxLog {
continue
}
if msgIndex > 0 {
// not the eth tx we want
msgIndex--
continue
}
return ParseTxLogsFromEvent(event)
}
return nil, fmt.Errorf("eth tx logs not found for message index %d", msgIndex)
}
// ParseTxLogsFromEvent parse tx logs from one event
func ParseTxLogsFromEvent(event abci.Event) ([]*ethtypes.Log, error) {
logs := make([]*evmtypes.Log, 0, len(event.Attributes))
for _, attr := range event.Attributes {
if !bytes.Equal(attr.Key, []byte(evmtypes.AttributeKeyTxLog)) {
continue
}
var log evmtypes.Log
if err := json.Unmarshal(attr.Value, &log); err != nil {
return nil, err
}
logs = append(logs, &log)
}
return evmtypes.LogsToEthereum(logs), nil
}
// ShouldIgnoreGasUsed returns true if the gasUsed in result should be ignored
// workaround for issue: https://github.com/cosmos/cosmos-sdk/issues/10832
func ShouldIgnoreGasUsed(res *abci.ResponseDeliverTx) bool {
return res.GetCode() == 11 && strings.Contains(res.GetLog(), "no block gas left to run tx: out of gas")
}
// GetLogsFromBlockResults returns the list of event logs from the tendermint block result response
func GetLogsFromBlockResults(blockRes *tmrpctypes.ResultBlockResults) ([][]*ethtypes.Log, error) {
blockLogs := [][]*ethtypes.Log{}
for _, txResult := range blockRes.TxsResults {
logs, err := AllTxLogsFromEvents(txResult.Events)
if err != nil {
return nil, err
}
blockLogs = append(blockLogs, logs...)
}
return blockLogs, nil
}
// GetHexProofs returns list of hex data of proof op
func GetHexProofs(proof *crypto.ProofOps) []string {
if proof == nil {
return []string{""}
}
proofs := []string{}
// check for proof
for _, p := range proof.Ops {
proof := ""
if len(p.Data) > 0 {
proof = hexutil.Encode(p.Data)
}
proofs = append(proofs, proof)
}
return proofs
}