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

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

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

tidy up

add feehistory-cap
  • Loading branch information
leejw51crypto committed Nov 17, 2021
1 parent b7e8dd8 commit 02fcb05
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 27 deletions.
10 changes: 9 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 Expand Up @@ -889,6 +892,11 @@ func (e *EVMBackend) RPCFilterCap() int32 {
return e.cfg.JSONRPC.FilterCap
}

// RPCFeeHistoryCap is the limit for total number of blocks that can be fetched
func (e *EVMBackend) RPCFeeHistgoryCap() int32 {
return e.cfg.JSONRPC.FeeHistoryCap
}

// RPCMinGasPrice returns the minimum gas price for a transaction obtained from
// the node config. If set value is 0, it will default to 20.

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)
maxBlockCount := int64(e.cfg.JSONRPC.FeeHistoryCap)
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 ethBlock == nil {
return nil, err
}

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

// tendermint block result
tendermintBlockResult, err := e.clientCtx.Client.BlockResults(e.ctx, &tendermintblock.Block.Height)
if tendermintBlockResult == 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 */
}
42 changes: 26 additions & 16 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const (

DefaultFilterCap int32 = 200

DefaultFeeHistoryCap int32 = 100

DefaultEVMTimeout = 5 * time.Second
// default 1.0 eth
DefaultTxFeeCap float64 = 1.0
Expand Down Expand Up @@ -72,6 +74,8 @@ type JSONRPCConfig struct {
TxFeeCap float64 `mapstructure:"txfee-cap"`
// FilterCap is the global cap for total number of filters that can be created.
FilterCap int32 `mapstructure:"filter-cap"`
// FeeHistoryCap is the global cap for total number of blocks that can be fetched
FeeHistoryCap int32 `mapstructure:"feehistory-cap"`
// Enable defines if the EVM RPC server should be enabled.
Enable bool `mapstructure:"enable"`
}
Expand Down Expand Up @@ -153,14 +157,15 @@ func GetDefaultAPINamespaces() []string {
// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultJSONRPCConfig() *JSONRPCConfig {
return &JSONRPCConfig{
Enable: true,
API: GetDefaultAPINamespaces(),
Address: DefaultJSONRPCAddress,
WsAddress: DefaultJSONRPCWsAddress,
GasCap: DefaultGasCap,
EVMTimeout: DefaultEVMTimeout,
TxFeeCap: DefaultTxFeeCap,
FilterCap: DefaultFilterCap,
Enable: true,
API: GetDefaultAPINamespaces(),
Address: DefaultJSONRPCAddress,
WsAddress: DefaultJSONRPCWsAddress,
GasCap: DefaultGasCap,
EVMTimeout: DefaultEVMTimeout,
TxFeeCap: DefaultTxFeeCap,
FilterCap: DefaultFilterCap,
FeeHistoryCap: DefaultFeeHistoryCap,
}
}

Expand All @@ -174,6 +179,10 @@ func (c JSONRPCConfig) Validate() error {
return errors.New("JSON-RPC filter-cap cannot be negative")
}

if c.FeeHistoryCap <= 0 {
return errors.New("JSON-RPC feehistory-cap cannot be negative or 0")
}

if c.TxFeeCap < 0 {
return errors.New("JSON-RPC tx fee cap cannot be negative")
}
Expand Down Expand Up @@ -230,14 +239,15 @@ func GetConfig(v *viper.Viper) Config {
Tracer: v.GetString("evm.tracer"),
},
JSONRPC: JSONRPCConfig{
Enable: v.GetBool("json-rpc.enable"),
API: v.GetStringSlice("json-rpc.api"),
Address: v.GetString("json-rpc.address"),
WsAddress: v.GetString("json-rpc.ws-address"),
GasCap: v.GetUint64("json-rpc.gas-cap"),
FilterCap: v.GetInt32("json-rpc.filter-cap"),
TxFeeCap: v.GetFloat64("json-rpc.txfee-cap"),
EVMTimeout: v.GetDuration("json-rpc.evm-timeout"),
Enable: v.GetBool("json-rpc.enable"),
API: v.GetStringSlice("json-rpc.api"),
Address: v.GetString("json-rpc.address"),
WsAddress: v.GetString("json-rpc.ws-address"),
GasCap: v.GetUint64("json-rpc.gas-cap"),
FilterCap: v.GetInt32("json-rpc.filter-cap"),
FeeHistoryCap: v.GetInt32("json-rpc.feehistory-cap"),
TxFeeCap: v.GetFloat64("json-rpc.txfee-cap"),
EVMTimeout: v.GetDuration("json-rpc.evm-timeout"),
},
TLS: TLSConfig{
CertificatePath: v.GetString("tls.certificate-path"),
Expand Down
4 changes: 4 additions & 0 deletions server/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ txfee-cap = {{ .JSONRPC.TxFeeCap }}
# FilterCap sets the global cap for total number of filters that can be created
filter-cap = {{ .JSONRPC.FilterCap }}
# FeeHistoryCap sets the global cap for total number of blocks that can be fetched
feehistory-cap = {{ .JSONRPC.FeeHistoryCap }}
###############################################################################
### TLS Configuration ###
###############################################################################
Expand Down
17 changes: 9 additions & 8 deletions server/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ const (

// JSON-RPC flags
const (
JSONRPCEnable = "json-rpc.enable"
JSONRPCAPI = "json-rpc.api"
JSONRPCAddress = "json-rpc.address"
JSONWsAddress = "json-rpc.ws-address"
JSONRPCGasCap = "json-rpc.gas-cap"
JSONRPCEVMTimeout = "json-rpc.evm-timeout"
JSONRPCTxFeeCap = "json-rpc.txfee-cap"
JSONRPCFilterCap = "json-rpc.filter-cap"
JSONRPCEnable = "json-rpc.enable"
JSONRPCAPI = "json-rpc.api"
JSONRPCAddress = "json-rpc.address"
JSONWsAddress = "json-rpc.ws-address"
JSONRPCGasCap = "json-rpc.gas-cap"
JSONRPCEVMTimeout = "json-rpc.evm-timeout"
JSONRPCTxFeeCap = "json-rpc.txfee-cap"
JSONRPCFilterCap = "json-rpc.filter-cap"
JSONRPFeeHistoryCap = "json-rpc.feehistory-cap"
)

// EVM flags
Expand Down
Loading

0 comments on commit 02fcb05

Please sign in to comment.