Skip to content

Commit

Permalink
Problem: missing json rpc of eth_feeHistory evmos#685
Browse files Browse the repository at this point in the history
add oracle backend

space ready

structure ok

refactoring

return feehistory

data flow ok

basefee

set gas used ratio

computing reward

add testing

add gas used

prepare data

fill reward

increase coin

fixing api

add mac

add launch

gas used ratio ok

print element

reward workes

reward working

fix panic

value correct

remove debugging log

tidy up

tidy up

remove oracle

tidy up

fix handler crash

add unit test

tidy up

add limit check

reformat

fix lint

fix lint

fix lint

fix lint

Update rpc/ethereum/backend/feebackend.go

thanks

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

Update rpc/ethereum/backend/feebackend.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

Update rpc/ethereum/backend/feebackend.go

thanks

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

Update rpc/ethereum/backend/feebackend.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

fix compile error

split lines

remove temporary string conversion

return error if gaslimit is 0

move OneFeeHistory to types

add comment

only err check
  • Loading branch information
leejw51crypto committed Nov 17, 2021
1 parent b7e8dd8 commit d6692e6
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 3 deletions.
5 changes: 4 additions & 1 deletion rpc/ethereum/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"

"google.golang.org/grpc"
Expand Down Expand Up @@ -42,6 +43,9 @@ import (
// Backend implements the functionality shared within namespaces.
// Implemented by EVMBackend.
type Backend interface {
// Fee API
FeeHistory(blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*types.FeeHistoryResult, error)

// General Ethereum API
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
Expand Down Expand Up @@ -76,7 +80,6 @@ type Backend interface {
GetLogs(hash common.Hash) ([][]*ethtypes.Log, error)
GetLogsByHeight(height *int64) ([][]*ethtypes.Log, error)
GetFilteredBlocks(from int64, to int64, filter [][]filters.BloomIV, filterAddresses bool) ([]int64, error)

ChainConfig() *params.ChainConfig
SetTxDefaults(args evmtypes.TransactionArgs) (evmtypes.TransactionArgs, error)
GetEthereumMsgsFromTendermintBlock(block *tmrpctypes.ResultBlock) []*evmtypes.MsgEthereumTx
Expand Down
197 changes: 197 additions & 0 deletions rpc/ethereum/backend/feebackend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package backend

import (
"fmt"
"math/big"
"sort"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tharsis/ethermint/rpc/ethereum/types"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
)

type (
txGasAndReward struct {
gasUsed uint64
reward *big.Int
}
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
}

// output: targetOneFeeHistory
func (e *EVMBackend) processBlock(
tendermintBlock *tmrpctypes.ResultBlock,
ethBlock *map[string]interface{},
rewardPercentiles []float64,
tendermintBlockResult *tmrpctypes.ResultBlockResults,
targetOneFeeHistory *rpctypes.OneFeeHistory) error {
blockHeight := tendermintBlock.Block.Height
blockBaseFee, err := e.BaseFee(blockHeight)
if err != nil {
return err
}

// set basefee
targetOneFeeHistory.BaseFee = blockBaseFee

// set gasused ratio
gasLimitUint64 := (*ethBlock)["gasLimit"].(hexutil.Uint64)
gasUsedBig := (*ethBlock)["gasUsed"].(*hexutil.Big)
gasusedfloat, _ := new(big.Float).SetInt(gasUsedBig.ToInt()).Float64()
var gasUsedRatio float64
if gasLimitUint64 > 0 {
gasUsedRatio = gasusedfloat / float64(gasLimitUint64)
} else {
return fmt.Errorf("gasLimit of block height %d should be bigger than 0 , current gaslimit %d", blockHeight, 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(2000)
}

// check tendermintTxs
tendermintTxs := tendermintBlock.Block.Txs
tendermintTxResults := tendermintBlockResult.TxsResults
tendermintTxCount := len(tendermintTxs)
sorter := make(sortGasAndReward, tendermintTxCount)

for i := 0; i < tendermintTxCount; i++ {
eachTendermintTx := tendermintTxs[i]
eachTendermintTxResult := tendermintTxResults[i]

tx, err := e.clientCtx.TxConfig.TxDecoder()(eachTendermintTx)
if err != nil {
e.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)
sorter[i] = txGasAndReward{gasUsed: txGasUsed, reward: reward}
break
}
}
sort.Sort(sorter)

var txIndex int
sumGasUsed := uint64(0)
if len(sorter) > 0 {
sumGasUsed = sorter[0].gasUsed
}
for i, p := range rewardPercentiles {
thresholdGasUsed := uint64(blockGasUsed * p / 100)
for sumGasUsed < thresholdGasUsed && txIndex < tendermintTxCount-1 {
txIndex++
sumGasUsed += sorter[txIndex].gasUsed
}

chosenReward := big.NewInt(0)
if 0 <= txIndex && txIndex < len(sorter) {
chosenReward = sorter[txIndex].reward
}
targetOneFeeHistory.Reward[i] = chosenReward
}

return nil
}

func (e *EVMBackend) FeeHistory(userBlockCount rpc.DecimalOrHex, /* number blocks to fetch, maximum is 100 */
lastBlock rpc.BlockNumber, /* the block to start search , to oldest */
rewardPercentiles []float64) /* percentiles to fetch reward */ (*rpctypes.FeeHistoryResult, error) {
blockEnd := int64(lastBlock)

if blockEnd <= 0 {
blockNumber, err := e.BlockNumber()
if err != nil {
return nil, err
}
blockEnd = int64(blockNumber)
}
userBlockCountInt := int64(userBlockCount)
const maxBlockCount = 100
if userBlockCountInt > maxBlockCount {
return nil, fmt.Errorf("FeeHistory user block count %d higher than %d", userBlockCountInt, maxBlockCount)
}
blockStart := blockEnd - userBlockCountInt
if blockStart < 0 {
blockStart = 0
}

blockCount := blockEnd - blockStart

OldestBlock := (*hexutil.Big)(big.NewInt(blockStart))

// prepare space
Reward := make([][]*hexutil.Big, blockCount)
rewardcount := len(rewardPercentiles)
for i := 0; i < int(blockCount); i++ {
Reward[i] = make([]*hexutil.Big, rewardcount)
}
thisBaseFee := make([]*hexutil.Big, blockCount)
thisGasUsedRatio := make([]float64, blockCount)

// fetch block
for blockID := blockStart; blockID < blockEnd; blockID++ {
index := int32(blockID - blockStart)
// eth block
ethBlock, err := e.GetBlockByNumber(rpctypes.BlockNumber(blockID), true)
if err != nil {
return nil, err
}

// tendermint block
tendermintblock, err := e.GetTendermintBlockByNumber(rpctypes.BlockNumber(blockID))
if err != nil {
return nil, err
}

// tendermint block result
tendermintBlockResult, err := e.clientCtx.Client.BlockResults(e.ctx, &tendermintblock.Block.Height)
if err != nil {
e.logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error())
return nil, err
}

onefeehistory := rpctypes.OneFeeHistory{}
err = e.processBlock(tendermintblock, &ethBlock, rewardPercentiles, tendermintBlockResult, &onefeehistory)
if err != nil {
return nil, err
}

// copy
thisBaseFee[index] = (*hexutil.Big)(onefeehistory.BaseFee)
thisGasUsedRatio[index] = onefeehistory.GasUsedRatio
for j := 0; j < rewardcount; j++ {
Reward[index][j] = (*hexutil.Big)(onefeehistory.Reward[j])
}

}

feeHistory := rpctypes.FeeHistoryResult{
OldestBlock: OldestBlock,
Reward: Reward,
BaseFee: thisBaseFee,
GasUsedRatio: thisGasUsedRatio,
}
return &feeHistory, nil
}
3 changes: 1 addition & 2 deletions rpc/ethereum/namespaces/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,7 @@ func (e *PublicAPI) MaxPriorityFeePerGas() (*hexutil.Big, error) {

func (e *PublicAPI) FeeHistory(blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*rpctypes.FeeHistoryResult, error) {
e.logger.Debug("eth_feeHistory")

return nil, fmt.Errorf("eth_feeHistory not implemented")
return e.backend.FeeHistory(blockCount, lastBlock, rewardPercentiles)
}

// Accounts returns the list of accounts available to this node.
Expand Down
8 changes: 8 additions & 0 deletions rpc/ethereum/types/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -79,3 +81,9 @@ type SignTransactionResult struct {
Raw hexutil.Bytes `json:"raw"`
Tx *ethtypes.Transaction `json:"tx"`
}

type OneFeeHistory struct {
BaseFee *big.Int /* base fee for each block */
Reward []*big.Int /* each element of the array will have the tip provided to miners for the percentile given */
GasUsedRatio float64 /* the ratio of gas used to gas limit for each block */
}
23 changes: 23 additions & 0 deletions tests/rpc/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1053,3 +1053,26 @@ func TestEth_EthResend(t *testing.T) {
_, rpcerror := callWithError("eth_resend", param)
require.Equal(t, "transaction 0x3bf28b46ee1bb3925e50ec6003f899f95913db4b0f579c4e7e887efebf9ecd1b not found", fmt.Sprintf("%s", rpcerror))
}

func TestEth_FeeHistory(t *testing.T) {

params := make([]interface{}, 0)
params = append(params, 4)
params = append(params, "0x1c")
params = append(params, []int{25, 75})

rpcRes := call(t, "eth_feeHistory", params)

info := make(map[string]interface{})
err := json.Unmarshal(rpcRes.Result, &info)
require.NoError(t, err)
reward := info["reward"].([]interface{})
baseFeePerGas := info["baseFeePerGas"].([]interface{})
gasUsedRatio := info["gasUsedRatio"].([]interface{})

require.Equal(t, info["oldestBlock"].(string), "0x18")
require.Equal(t, 4, len(gasUsedRatio))
require.Equal(t, 4, len(baseFeePerGas))
require.Equal(t, 4, len(reward))

}

0 comments on commit d6692e6

Please sign in to comment.