Skip to content

Commit fddae70

Browse files
committed
Merge remote-tracking branch 'upstream/main' into tests/evm-tools-compatibility
2 parents 4088932 + 66dd661 commit fddae70

File tree

17 files changed

+534
-135
lines changed

17 files changed

+534
-135
lines changed

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ go 1.23.8
44

55
require (
66
cosmossdk.io/api v0.9.2
7-
cosmossdk.io/client/v2 v2.0.0-beta.7
87
cosmossdk.io/core v0.11.3
98
cosmossdk.io/errors v1.0.2
109
cosmossdk.io/log v1.6.0

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,8 +616,6 @@ cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT
616616
cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
617617
cosmossdk.io/api v0.9.2 h1:9i9ptOBdmoIEVEVWLtYYHjxZonlF/aOVODLFaxpmNtg=
618618
cosmossdk.io/api v0.9.2/go.mod h1:CWt31nVohvoPMTlPv+mMNCtC0a7BqRdESjCsstHcTkU=
619-
cosmossdk.io/client/v2 v2.0.0-beta.7 h1:O0PfZL5kC3Sp54wZASLNihQ612Gd6duMp11aM9wawNg=
620-
cosmossdk.io/client/v2 v2.0.0-beta.7/go.mod h1:TzwwrzeK+AfSVSESVEIOYO/9xuCh1fPv0HgeocmfVnM=
621619
cosmossdk.io/collections v1.2.1 h1:mAlNMs5vJwkda4TA+k5q/43p24RVAQ/qyDrjANu3BXE=
622620
cosmossdk.io/collections v1.2.1/go.mod h1:PSsEJ/fqny0VPsHLFT6gXDj/2C1tBOTS9eByK0+PBFU=
623621
cosmossdk.io/core v0.11.3 h1:mei+MVDJOwIjIniaKelE3jPDqShCc/F4LkNNHh+4yfo=

precompiles/slashing/query.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ const (
1717
GetParamsMethod = "getParams"
1818
)
1919

20-
// GetSigningInfo implements the query to get a validator's signing info.
20+
// GetSigningInfo handles the `getSigningInfo` precompile call.
21+
// It expects a single argument: the validator’s consensus address in hex format.
22+
// That address comes from the validator’s Tendermint ed25519 public key,
23+
// typically found in `$HOME/.evmd/config/priv_validator_key.json`.
2124
func (p *Precompile) GetSigningInfo(
2225
ctx sdk.Context,
2326
method *abi.Method,
@@ -34,7 +37,10 @@ func (p *Precompile) GetSigningInfo(
3437
return nil, err
3538
}
3639

37-
out := new(SigningInfoOutput).FromResponse(res)
40+
out, err := new(SigningInfoOutput).FromResponse(res)
41+
if err != nil {
42+
return nil, err
43+
}
3844
return method.Outputs.Pack(out.SigningInfo)
3945
}
4046

@@ -55,7 +61,10 @@ func (p *Precompile) GetSigningInfos(
5561
return nil, err
5662
}
5763

58-
out := new(SigningInfosOutput).FromResponse(res)
64+
out, err := new(SigningInfosOutput).FromResponse(res)
65+
if err != nil {
66+
return nil, err
67+
}
5968
return method.Outputs.Pack(out.SigningInfos, out.PageResponse)
6069
}
6170

precompiles/slashing/types.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,32 @@ func ParseSigningInfosArgs(method *abi.Method, args []interface{}) (*slashingtyp
7373
}, nil
7474
}
7575

76-
func (sio *SigningInfoOutput) FromResponse(res *slashingtypes.QuerySigningInfoResponse) *SigningInfoOutput {
76+
func (sio *SigningInfoOutput) FromResponse(res *slashingtypes.QuerySigningInfoResponse) (*SigningInfoOutput, error) {
77+
consAddr, err := types.ConsAddressFromBech32(res.ValSigningInfo.Address)
78+
if err != nil {
79+
return nil, fmt.Errorf("error parsing consensus address: %w", err)
80+
}
81+
7782
sio.SigningInfo = SigningInfo{
78-
ValidatorAddress: common.BytesToAddress([]byte(res.ValSigningInfo.Address)),
83+
ValidatorAddress: common.BytesToAddress(consAddr.Bytes()),
7984
StartHeight: res.ValSigningInfo.StartHeight,
8085
IndexOffset: res.ValSigningInfo.IndexOffset,
8186
JailedUntil: res.ValSigningInfo.JailedUntil.Unix(),
8287
Tombstoned: res.ValSigningInfo.Tombstoned,
8388
MissedBlocksCounter: res.ValSigningInfo.MissedBlocksCounter,
8489
}
85-
return sio
90+
return sio, nil
8691
}
8792

88-
func (sio *SigningInfosOutput) FromResponse(res *slashingtypes.QuerySigningInfosResponse) *SigningInfosOutput {
93+
func (sio *SigningInfosOutput) FromResponse(res *slashingtypes.QuerySigningInfosResponse) (*SigningInfosOutput, error) {
8994
sio.SigningInfos = make([]SigningInfo, len(res.Info))
9095
for i, info := range res.Info {
96+
consAddr, err := types.ConsAddressFromBech32(info.Address)
97+
if err != nil {
98+
return nil, fmt.Errorf("error parsing consensus address: %w", err)
99+
}
91100
sio.SigningInfos[i] = SigningInfo{
92-
ValidatorAddress: common.BytesToAddress([]byte(info.Address)),
101+
ValidatorAddress: common.BytesToAddress(consAddr.Bytes()),
93102
StartHeight: info.StartHeight,
94103
IndexOffset: info.IndexOffset,
95104
JailedUntil: info.JailedUntil.Unix(),
@@ -103,7 +112,7 @@ func (sio *SigningInfosOutput) FromResponse(res *slashingtypes.QuerySigningInfos
103112
Total: res.Pagination.Total,
104113
}
105114
}
106-
return sio
115+
return sio, nil
107116
}
108117

109118
// ValidatorUnjailed defines the data structure for the ValidatorUnjailed event.

rpc/backend/backend.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/ethereum/go-ethereum/common"
1010
"github.com/ethereum/go-ethereum/common/hexutil"
11+
"github.com/ethereum/go-ethereum/common/math"
1112
ethtypes "github.com/ethereum/go-ethereum/core/types"
1213
"github.com/ethereum/go-ethereum/params"
1314
"github.com/ethereum/go-ethereum/rpc"
@@ -91,7 +92,7 @@ type EVMBackend interface {
9192
CurrentHeader() (*ethtypes.Header, error)
9293
PendingTransactions() ([]*sdk.Tx, error)
9394
GetCoinbase() (sdk.AccAddress, error)
94-
FeeHistory(blockCount, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*rpctypes.FeeHistoryResult, error)
95+
FeeHistory(blockCount math.HexOrDecimal64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*rpctypes.FeeHistoryResult, error)
9596
SuggestGasTipCap(baseFee *big.Int) (*big.Int, error)
9697

9798
// Tx Info
@@ -124,6 +125,27 @@ type EVMBackend interface {
124125

125126
var _ BackendI = (*Backend)(nil)
126127

128+
// ProcessBlocker is a function type that processes a block and its associated data
129+
// for fee history calculation. It takes a Tendermint block, its corresponding
130+
// Ethereum block representation, reward percentiles for fee estimation,
131+
// block results, and a target fee history entry to populate.
132+
//
133+
// Parameters:
134+
// - tendermintBlock: The raw Tendermint block data
135+
// - ethBlock: The Ethereum-formatted block representation
136+
// - rewardPercentiles: Percentiles used for fee reward calculation
137+
// - tendermintBlockResult: Block execution results from Tendermint
138+
// - targetOneFeeHistory: The fee history entry to be populated
139+
//
140+
// Returns an error if block processing fails.
141+
type ProcessBlocker func(
142+
tendermintBlock *tmrpctypes.ResultBlock,
143+
ethBlock *map[string]interface{},
144+
rewardPercentiles []float64,
145+
tendermintBlockResult *tmrpctypes.ResultBlockResults,
146+
targetOneFeeHistory *rpctypes.OneFeeHistory,
147+
) error
148+
127149
// Backend implements the BackendI interface
128150
type Backend struct {
129151
Ctx context.Context
@@ -135,6 +157,7 @@ type Backend struct {
135157
Cfg config.Config
136158
AllowUnprotectedTxs bool
137159
Indexer cosmosevmtypes.EVMTxIndexer
160+
ProcessBlocker ProcessBlocker
138161
}
139162

140163
// NewBackend creates a new Backend instance for cosmos and ethereum namespaces
@@ -155,7 +178,7 @@ func NewBackend(
155178
panic(fmt.Sprintf("invalid rpc client, expected: tmrpcclient.SignClient, got: %T", clientCtx.Client))
156179
}
157180

158-
return &Backend{
181+
b := &Backend{
159182
Ctx: context.Background(),
160183
ClientCtx: clientCtx,
161184
RPCClient: rpcClient,
@@ -166,4 +189,6 @@ func NewBackend(
166189
AllowUnprotectedTxs: allowUnprotectedTxs,
167190
Indexer: indexer,
168191
}
192+
b.ProcessBlocker = b.ProcessBlock
193+
return b
169194
}

rpc/backend/chain_info.go

Lines changed: 114 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package backend
22

33
import (
44
"fmt"
5+
gomath "math"
56
"math/big"
7+
"sync"
68

79
"github.com/ethereum/go-ethereum/common/hexutil"
10+
"github.com/ethereum/go-ethereum/common/math"
811
ethtypes "github.com/ethereum/go-ethereum/core/types"
912
"github.com/ethereum/go-ethereum/params"
1013
"github.com/ethereum/go-ethereum/rpc"
@@ -17,7 +20,8 @@ import (
1720
feemarkettypes "github.com/cosmos/evm/x/feemarket/types"
1821
evmtypes "github.com/cosmos/evm/x/vm/types"
1922

20-
"cosmossdk.io/math"
23+
errorsmod "cosmossdk.io/errors"
24+
sdkmath "cosmossdk.io/math"
2125

2226
sdk "github.com/cosmos/cosmos-sdk/types"
2327
)
@@ -69,7 +73,7 @@ func (b *Backend) BaseFee(blockRes *cmtrpctypes.ResultBlockResults) (*big.Int, e
6973
for i := len(blockRes.FinalizeBlockEvents) - 1; i >= 0; i-- {
7074
evt := blockRes.FinalizeBlockEvents[i]
7175
if evt.Type == evmtypes.EventTypeFeeMarket && len(evt.Attributes) > 0 {
72-
baseFee, ok := math.NewIntFromString(evt.Attributes[0].Value)
76+
baseFee, ok := sdkmath.NewIntFromString(evt.Attributes[0].Value)
7377
if ok {
7478
return baseFee.BigInt(), nil
7579
}
@@ -143,20 +147,45 @@ func (b *Backend) GetCoinbase() (sdk.AccAddress, error) {
143147
return address, nil
144148
}
145149

150+
var (
151+
errInvalidPercentile = fmt.Errorf("invalid reward percentile")
152+
errRequestBeyondHead = fmt.Errorf("request beyond head block")
153+
)
154+
146155
// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
147156
func (b *Backend) FeeHistory(
148-
userBlockCount, // number blocks to fetch, maximum is 100
157+
userBlockCount math.HexOrDecimal64, // number blocks to fetch, maximum is 100
149158
lastBlock rpc.BlockNumber, // the block to start search , to oldest
150159
rewardPercentiles []float64, // percentiles to fetch reward
151160
) (*rpctypes.FeeHistoryResult, error) {
152-
blockEnd := int64(lastBlock) //#nosec G115 -- checked for int overflow already
153-
154-
if blockEnd < 0 {
155-
blockNumber, err := b.BlockNumber()
156-
if err != nil {
157-
return nil, err
161+
for i, p := range rewardPercentiles {
162+
if p < 0 || p > 100 {
163+
return nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
158164
}
159-
blockEnd = int64(blockNumber) //#nosec G115 -- checked for int overflow already
165+
if i > 0 && p < rewardPercentiles[i-1] {
166+
return nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
167+
}
168+
}
169+
blkNumber, err := b.BlockNumber()
170+
if err != nil {
171+
return nil, err
172+
}
173+
blockNumber := int64(blkNumber) //#nosec G115
174+
blockEnd := int64(lastBlock) //#nosec G115
175+
176+
switch lastBlock {
177+
case rpc.EarliestBlockNumber:
178+
blockEnd = 0
179+
case rpc.SafeBlockNumber, rpc.FinalizedBlockNumber, rpc.LatestBlockNumber, rpc.PendingBlockNumber:
180+
blockEnd = blockNumber
181+
default:
182+
if blockEnd < 0 {
183+
blockEnd = blockNumber
184+
}
185+
}
186+
187+
if blockNumber < blockEnd {
188+
return nil, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, blockEnd, blockNumber)
160189
}
161190

162191
blocks := int64(userBlockCount) // #nosec G115 -- checked for int overflow already
@@ -165,7 +194,7 @@ func (b *Backend) FeeHistory(
165194
return nil, fmt.Errorf("FeeHistory user block count %d higher than %d", blocks, maxBlockCount)
166195
}
167196

168-
if blockEnd+1 < blocks {
197+
if blockEnd < gomath.MaxInt64 && blockEnd+1 < blocks {
169198
blocks = blockEnd + 1
170199
}
171200
// Ensure not trying to retrieve before genesis.
@@ -184,46 +213,85 @@ func (b *Backend) FeeHistory(
184213

185214
// rewards should only be calculated if reward percentiles were included
186215
calculateRewards := rewardCount != 0
216+
const maxBlockFetchers = 4
217+
for blockID := blockStart; blockID <= blockEnd; blockID += maxBlockFetchers {
218+
wg := sync.WaitGroup{}
219+
wgDone := make(chan bool)
220+
chanErr := make(chan error)
221+
for i := 0; i < maxBlockFetchers; i++ {
222+
if blockID+int64(i) >= blockEnd+1 {
223+
break
224+
}
225+
value := blockID - blockStart + int64(i)
226+
if value > gomath.MaxInt32 || value < gomath.MinInt32 {
227+
return nil, fmt.Errorf("integer overflow: calculated value %d exceeds int32 limits", value)
228+
}
229+
wg.Add(1)
230+
go func(index int32) {
231+
defer func() {
232+
if r := recover(); r != nil {
233+
err = errorsmod.Wrapf(errorsmod.ErrPanic, "%v", r)
234+
b.Logger.Error("FeeHistory panicked", "error", err)
235+
chanErr <- err
236+
}
237+
wg.Done()
238+
}()
239+
// fetch block
240+
// tendermint block
241+
blockNum := rpctypes.BlockNumber(blockStart + int64(index))
242+
tendermintblock, err := b.TendermintBlockByNumber(blockNum)
243+
if tendermintblock == nil {
244+
chanErr <- err
245+
return
246+
}
187247

188-
// fetch block
189-
for blockID := blockStart; blockID <= blockEnd; blockID++ {
190-
index := int32(blockID - blockStart) // #nosec G115
191-
// tendermint block
192-
tendermintblock, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(blockID))
193-
if tendermintblock == nil {
194-
return nil, err
195-
}
196-
197-
// eth block
198-
ethBlock, err := b.GetBlockByNumber(rpctypes.BlockNumber(blockID), true)
199-
if ethBlock == nil {
200-
return nil, err
201-
}
248+
// eth block
249+
ethBlock, err := b.GetBlockByNumber(blockNum, true)
250+
if ethBlock == nil {
251+
chanErr <- err
252+
return
253+
}
202254

203-
// tendermint block result
204-
tendermintBlockResult, err := b.RPCClient.BlockResults(b.Ctx, &tendermintblock.Block.Height)
205-
if tendermintBlockResult == nil {
206-
b.Logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error())
207-
return nil, err
208-
}
255+
// tendermint block result
256+
tendermintBlockResult, err := b.TendermintBlockResultByNumber(&tendermintblock.Block.Height)
257+
if tendermintBlockResult == nil {
258+
b.Logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error())
259+
chanErr <- err
260+
return
261+
}
209262

210-
oneFeeHistory := rpctypes.OneFeeHistory{}
211-
err = b.processBlock(tendermintblock, &ethBlock, rewardPercentiles, tendermintBlockResult, &oneFeeHistory)
212-
if err != nil {
213-
return nil, err
214-
}
263+
oneFeeHistory := rpctypes.OneFeeHistory{}
264+
err = b.ProcessBlocker(tendermintblock, &ethBlock, rewardPercentiles, tendermintBlockResult, &oneFeeHistory)
265+
if err != nil {
266+
chanErr <- err
267+
return
268+
}
215269

216-
// copy
217-
thisBaseFee[index] = (*hexutil.Big)(oneFeeHistory.BaseFee)
218-
thisBaseFee[index+1] = (*hexutil.Big)(oneFeeHistory.NextBaseFee)
219-
thisGasUsedRatio[index] = oneFeeHistory.GasUsedRatio
220-
if calculateRewards {
221-
for j := 0; j < rewardCount; j++ {
222-
reward[index][j] = (*hexutil.Big)(oneFeeHistory.Reward[j])
223-
if reward[index][j] == nil {
224-
reward[index][j] = (*hexutil.Big)(big.NewInt(0))
270+
// copy
271+
thisBaseFee[index] = (*hexutil.Big)(oneFeeHistory.BaseFee)
272+
// only use NextBaseFee as last item to avoid concurrent write
273+
if int(index) == len(thisBaseFee)-2 {
274+
thisBaseFee[index+1] = (*hexutil.Big)(oneFeeHistory.NextBaseFee)
225275
}
226-
}
276+
thisGasUsedRatio[index] = oneFeeHistory.GasUsedRatio
277+
if calculateRewards {
278+
for j := 0; j < rewardCount; j++ {
279+
reward[index][j] = (*hexutil.Big)(oneFeeHistory.Reward[j])
280+
if reward[index][j] == nil {
281+
reward[index][j] = (*hexutil.Big)(big.NewInt(0))
282+
}
283+
}
284+
}
285+
}(int32(value))
286+
}
287+
go func() {
288+
wg.Wait()
289+
close(wgDone)
290+
}()
291+
select {
292+
case <-wgDone:
293+
case err := <-chanErr:
294+
return nil, err
227295
}
228296
}
229297

0 commit comments

Comments
 (0)