forked from EscanBE/evermint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chain_info.go
273 lines (236 loc) · 8.8 KB
/
chain_info.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package backend
import (
"errors"
"fmt"
tmrpcclient "github.com/cometbft/cometbft/rpc/client"
"math/big"
"strconv"
rpctypes "github.com/servprotocolorg/serv/v12/rpc/types"
"github.com/servprotocolorg/serv/v12/types"
evmtypes "github.com/servprotocolorg/serv/v12/x/evm/types"
feemarkettypes "github.com/servprotocolorg/serv/v12/x/feemarket/types"
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
// ChainID is the EIP-155 replay-protection chain id for the current ethereum chain config.
func (b *Backend) ChainID() (*hexutil.Big, error) {
eip155ChainID, err := types.ParseChainID(b.clientCtx.ChainID)
if err != nil {
panic(err)
}
// if current block is at or past the EIP-155 replay-protection fork block, return chainID from config
bn, err := b.BlockNumber()
if err != nil {
b.logger.Debug("failed to fetch latest block number", "error", err.Error())
return (*hexutil.Big)(eip155ChainID), nil
}
if config := b.ChainConfig(); config.IsEIP155(new(big.Int).SetUint64(uint64(bn))) {
return (*hexutil.Big)(config.ChainID), nil
}
return nil, fmt.Errorf("chain not synced beyond EIP-155 replay-protection fork block")
}
// ChainConfig returns the latest ethereum chain configuration
func (b *Backend) ChainConfig() *params.ChainConfig {
params, err := b.queryClient.Params(b.ctx, &evmtypes.QueryParamsRequest{})
if err != nil {
return nil
}
return params.Params.ChainConfig.EthereumConfig(b.chainID)
}
// GlobalMinGasPrice returns MinGasPrice param from FeeMarket
func (b *Backend) GlobalMinGasPrice() (sdk.Dec, error) {
res, err := b.queryClient.FeeMarket.Params(b.ctx, &feemarkettypes.QueryParamsRequest{})
if err != nil {
return sdk.ZeroDec(), err
}
return res.Params.MinGasPrice, nil
}
// BaseFee returns the base fee tracked by the Fee Market module.
// If the base fee is not enabled globally, the query returns nil.
// If the London hard fork is not activated at the current height, the query will
// return nil.
func (b *Backend) BaseFee(blockRes *tmrpctypes.ResultBlockResults) (*big.Int, error) {
// return BaseFee if London hard fork is activated and feemarket is enabled
res, err := b.queryClient.BaseFee(rpctypes.ContextWithHeight(blockRes.Height), &evmtypes.QueryBaseFeeRequest{})
if err != nil || res.BaseFee == nil {
// we can't tell if it's london HF not enabled or the state is pruned,
// in either case, we'll fallback to parsing from begin blocker event,
// faster to iterate reversely
for i := len(blockRes.BeginBlockEvents) - 1; i >= 0; i-- {
evt := blockRes.BeginBlockEvents[i]
if evt.Type == feemarkettypes.EventTypeFeeMarket && len(evt.Attributes) > 0 {
baseFee, err := strconv.ParseInt(string(evt.Attributes[0].Value), 10, 64)
if err == nil {
return big.NewInt(baseFee), nil
}
break
}
}
return nil, err
}
if res.BaseFee == nil {
return nil, nil
}
return res.BaseFee.BigInt(), nil
}
// CurrentHeader returns the latest block header
func (b *Backend) CurrentHeader() *ethtypes.Header {
header, _ := b.HeaderByNumber(rpctypes.EthLatestBlockNumber) // #nosec G703
return header
}
// PendingTransactions returns the transactions that are in the transaction pool
// and have a from address that is one of the accounts this node manages.
func (b *Backend) PendingTransactions() ([]*sdk.Tx, error) {
mc, ok := b.clientCtx.Client.(tmrpcclient.MempoolClient)
if !ok {
return nil, errors.New("invalid rpc client")
}
res, err := mc.UnconfirmedTxs(b.ctx, nil)
if err != nil {
return nil, err
}
result := make([]*sdk.Tx, 0, len(res.Txs))
for _, txBz := range res.Txs {
tx, err := b.clientCtx.TxConfig.TxDecoder()(txBz)
if err != nil {
return nil, err
}
result = append(result, &tx)
}
return result, nil
}
// GetCoinbase is the address that staking rewards will be send to (alias for Etherbase).
func (b *Backend) GetCoinbase() (sdk.AccAddress, error) {
node, err := b.clientCtx.GetNode()
if err != nil {
return nil, err
}
status, err := node.Status(b.ctx)
if err != nil {
return nil, err
}
req := &evmtypes.QueryValidatorAccountRequest{
ConsAddress: sdk.ConsAddress(status.ValidatorInfo.Address).String(),
}
res, err := b.queryClient.ValidatorAccount(b.ctx, req)
if err != nil {
return nil, err
}
address, _ := sdk.AccAddressFromBech32(res.AccountAddress) // #nosec G703
return address, nil
}
// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
func (b *Backend) 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) //#nosec G701 -- checked for int overflow already
if blockEnd < 0 {
blockNumber, err := b.BlockNumber()
if err != nil {
return nil, err
}
blockEnd = int64(blockNumber) //#nosec G701 -- checked for int overflow already
}
blocks := int64(userBlockCount) // #nosec G701 -- checked for int overflow already
maxBlockCount := int64(b.cfg.JSONRPC.FeeHistoryCap) // #nosec G701 -- checked for int overflow already
if blocks > maxBlockCount {
return nil, fmt.Errorf("FeeHistory user block count %d higher than %d", blocks, maxBlockCount)
}
if blockEnd+1 < blocks {
blocks = blockEnd + 1
}
// Ensure not trying to retrieve before genesis.
blockStart := blockEnd + 1 - blocks
oldestBlock := (*hexutil.Big)(big.NewInt(blockStart))
// prepare space
reward := make([][]*hexutil.Big, blocks)
rewardCount := len(rewardPercentiles)
for i := 0; i < int(blocks); i++ {
reward[i] = make([]*hexutil.Big, rewardCount)
}
thisBaseFee := make([]*hexutil.Big, blocks+1)
thisGasUsedRatio := make([]float64, blocks)
// rewards should only be calculated if reward percentiles were included
calculateRewards := rewardCount != 0
// fetch block
for blockID := blockStart; blockID <= blockEnd; blockID++ {
index := int32(blockID - blockStart) // #nosec G701
// tendermint block
tendermintblock, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(blockID))
if tendermintblock == nil {
return nil, err
}
// eth block
ethBlock, err := b.GetBlockByNumber(rpctypes.BlockNumber(blockID), true)
if ethBlock == nil {
return nil, err
}
// tendermint block result
tendermintBlockResult, err := b.TendermintBlockResultByNumber(&tendermintblock.Block.Height)
if tendermintBlockResult == nil {
b.logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error())
return nil, err
}
oneFeeHistory := rpctypes.OneFeeHistory{}
err = b.processBlock(tendermintblock, ðBlock, rewardPercentiles, tendermintBlockResult, &oneFeeHistory)
if err != nil {
return nil, err
}
// copy
thisBaseFee[index] = (*hexutil.Big)(oneFeeHistory.BaseFee)
thisBaseFee[index+1] = (*hexutil.Big)(oneFeeHistory.NextBaseFee)
thisGasUsedRatio[index] = oneFeeHistory.GasUsedRatio
if calculateRewards {
for j := 0; j < rewardCount; j++ {
reward[index][j] = (*hexutil.Big)(oneFeeHistory.Reward[j])
if reward[index][j] == nil {
reward[index][j] = (*hexutil.Big)(big.NewInt(0))
}
}
}
}
feeHistory := rpctypes.FeeHistoryResult{
OldestBlock: oldestBlock,
BaseFee: thisBaseFee,
GasUsedRatio: thisGasUsedRatio,
}
if calculateRewards {
feeHistory.Reward = reward
}
return &feeHistory, nil
}
// SuggestGasTipCap returns the suggested tip cap
// Although we don't support tx prioritization yet, but we return a positive value to help client to
// mitigate the base fee changes.
func (b *Backend) SuggestGasTipCap(baseFee *big.Int) (*big.Int, error) {
if baseFee == nil {
// london hardfork not enabled or feemarket not enabled
return big.NewInt(0), nil
}
params, err := b.queryClient.FeeMarket.Params(b.ctx, &feemarkettypes.QueryParamsRequest{})
if err != nil {
return nil, err
}
// calculate the maximum base fee delta in current block, assuming all block gas limit is consumed
// ```
// GasTarget = GasLimit / ElasticityMultiplier
// Delta = BaseFee * (GasUsed - GasTarget) / GasTarget / Denominator
// ```
// The delta is at maximum when `GasUsed` is equal to `GasLimit`, which is:
// ```
// MaxDelta = BaseFee * (GasLimit - GasLimit / ElasticityMultiplier) / (GasLimit / ElasticityMultiplier) / Denominator
// = BaseFee * (ElasticityMultiplier - 1) / Denominator
// ```t
maxDelta := baseFee.Int64() * (int64(params.Params.ElasticityMultiplier) - 1) / int64(params.Params.BaseFeeChangeDenominator) // #nosec G701
if maxDelta < 0 {
// impossible if the parameter validation passed.
maxDelta = 0
}
return big.NewInt(maxDelta), nil
}