Skip to content

Commit

Permalink
improve tracer/simulation RPC
Browse files Browse the repository at this point in the history
  • Loading branch information
codchen committed Feb 19, 2025
1 parent 1232335 commit c520e0a
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 97 deletions.
8 changes: 5 additions & 3 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ type App struct {
optimisticProcessingInfo *OptimisticProcessingInfo

// batchVerifier *ante.SR25519BatchVerifier
txDecoder sdk.TxDecoder
txDecoder sdk.TxDecoder
AnteHandler sdk.AnteHandler

versionInfo version.Info

Expand Down Expand Up @@ -922,6 +923,7 @@ func New(
if err != nil {
panic(err)
}
app.AnteHandler = anteHandler

app.SetAnteHandler(anteHandler)
app.SetAnteDepGenerator(anteDepGenerator)
Expand Down Expand Up @@ -1842,7 +1844,7 @@ func (app *App) RegisterTendermintService(clientCtx client.Context) {
return ctx.WithIsEVM(true)
}
if app.evmRPCConfig.HTTPEnabled {
evmHTTPServer, err := evmrpc.NewEVMHTTPServer(app.Logger(), app.evmRPCConfig, clientCtx.Client, &app.EvmKeeper, ctxProvider, app.encodingConfig.TxConfig, DefaultNodeHome, nil)
evmHTTPServer, err := evmrpc.NewEVMHTTPServer(app.Logger(), app.evmRPCConfig, clientCtx.Client, &app.EvmKeeper, app.BaseApp, app.AnteHandler, ctxProvider, app.encodingConfig.TxConfig, DefaultNodeHome, nil)
if err != nil {
panic(err)
}
Expand All @@ -1852,7 +1854,7 @@ func (app *App) RegisterTendermintService(clientCtx client.Context) {
}

if app.evmRPCConfig.WSEnabled {
evmWSServer, err := evmrpc.NewEVMWebSocketServer(app.Logger(), app.evmRPCConfig, clientCtx.Client, &app.EvmKeeper, ctxProvider, app.encodingConfig.TxConfig, DefaultNodeHome)
evmWSServer, err := evmrpc.NewEVMWebSocketServer(app.Logger(), app.evmRPCConfig, clientCtx.Client, &app.EvmKeeper, app.BaseApp, app.AnteHandler, ctxProvider, app.encodingConfig.TxConfig, DefaultNodeHome)
if err != nil {
panic(err)
}
Expand Down
6 changes: 4 additions & 2 deletions evmrpc/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"time"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
Expand Down Expand Up @@ -34,15 +35,16 @@ type SendConfig struct {
slow bool
}

func NewSendAPI(tmClient rpcclient.Client, txConfig client.TxConfig, sendConfig *SendConfig, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, homeDir string, simulateConfig *SimulateConfig, connectionType ConnectionType) *SendAPI {
func NewSendAPI(tmClient rpcclient.Client, txConfig client.TxConfig, sendConfig *SendConfig, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, homeDir string, simulateConfig *SimulateConfig, app *baseapp.BaseApp,
antehandler sdk.AnteHandler, connectionType ConnectionType) *SendAPI {
return &SendAPI{
tmClient: tmClient,
txConfig: txConfig,
sendConfig: sendConfig,
keeper: k,
ctxProvider: ctxProvider,
homeDir: homeDir,
backend: NewBackend(ctxProvider, k, txConfig.TxDecoder(), tmClient, simulateConfig),
backend: NewBackend(ctxProvider, k, txConfig.TxDecoder(), tmClient, simulateConfig, app, antehandler),
connectionType: connectionType,
}
}
Expand Down
17 changes: 11 additions & 6 deletions evmrpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"strings"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -30,6 +31,8 @@ func NewEVMHTTPServer(
config Config,
tmClient rpcclient.Client,
k *keeper.Keeper,
app *baseapp.BaseApp,
antehandler sdk.AnteHandler,
ctxProvider func(int64) sdk.Context,
txConfig client.TxConfig,
homeDir string,
Expand All @@ -45,18 +48,18 @@ func NewEVMHTTPServer(
return nil, err
}
simulateConfig := &SimulateConfig{GasCap: config.SimulationGasLimit, EVMTimeout: config.SimulationEVMTimeout}
sendAPI := NewSendAPI(tmClient, txConfig, &SendConfig{slow: config.Slow}, k, ctxProvider, homeDir, simulateConfig, ConnectionTypeHTTP)
sendAPI := NewSendAPI(tmClient, txConfig, &SendConfig{slow: config.Slow}, k, ctxProvider, homeDir, simulateConfig, app, antehandler, ConnectionTypeHTTP)
ctx := ctxProvider(LatestCtxHeight)

txAPI := NewTransactionAPI(tmClient, k, ctxProvider, txConfig, homeDir, ConnectionTypeHTTP)
debugAPI := NewDebugAPI(tmClient, k, ctxProvider, txConfig.TxDecoder(), simulateConfig, ConnectionTypeHTTP)
debugAPI := NewDebugAPI(tmClient, k, ctxProvider, txConfig.TxDecoder(), simulateConfig, app, antehandler, ConnectionTypeHTTP)
if isPanicOrSyntheticTxFunc == nil {
isPanicOrSyntheticTxFunc = func(ctx context.Context, hash common.Hash) (bool, error) {
return debugAPI.isPanicOrSyntheticTx(ctx, hash)
}
}
seiTxAPI := NewSeiTransactionAPI(tmClient, k, ctxProvider, txConfig, homeDir, ConnectionTypeHTTP, isPanicOrSyntheticTxFunc)
seiDebugAPI := NewSeiDebugAPI(tmClient, k, ctxProvider, txConfig.TxDecoder(), simulateConfig, ConnectionTypeHTTP)
seiDebugAPI := NewSeiDebugAPI(tmClient, k, ctxProvider, txConfig.TxDecoder(), simulateConfig, app, antehandler, ConnectionTypeHTTP)

apis := []rpc.API{
{
Expand Down Expand Up @@ -97,7 +100,7 @@ func NewEVMHTTPServer(
},
{
Namespace: "eth",
Service: NewSimulationAPI(ctxProvider, k, txConfig.TxDecoder(), tmClient, simulateConfig, ConnectionTypeHTTP),
Service: NewSimulationAPI(ctxProvider, k, txConfig.TxDecoder(), tmClient, simulateConfig, app, antehandler, ConnectionTypeHTTP),
},
{
Namespace: "net",
Expand Down Expand Up @@ -157,6 +160,8 @@ func NewEVMWebSocketServer(
config Config,
tmClient rpcclient.Client,
k *keeper.Keeper,
app *baseapp.BaseApp,
antehandler sdk.AnteHandler,
ctxProvider func(int64) sdk.Context,
txConfig client.TxConfig,
homeDir string,
Expand Down Expand Up @@ -194,11 +199,11 @@ func NewEVMWebSocketServer(
},
{
Namespace: "eth",
Service: NewSendAPI(tmClient, txConfig, &SendConfig{slow: config.Slow}, k, ctxProvider, homeDir, simulateConfig, ConnectionTypeWS),
Service: NewSendAPI(tmClient, txConfig, &SendConfig{slow: config.Slow}, k, ctxProvider, homeDir, simulateConfig, app, antehandler, ConnectionTypeWS),
},
{
Namespace: "eth",
Service: NewSimulationAPI(ctxProvider, k, txConfig.TxDecoder(), tmClient, simulateConfig, ConnectionTypeWS),
Service: NewSimulationAPI(ctxProvider, k, txConfig.TxDecoder(), tmClient, simulateConfig, app, antehandler, ConnectionTypeWS),
},
{
Namespace: "net",
Expand Down
6 changes: 3 additions & 3 deletions evmrpc/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ func init() {
if err != nil {
panic(err)
}
HttpServer, err := evmrpc.NewEVMHTTPServer(infoLog, goodConfig, &MockClient{}, EVMKeeper, ctxProvider, TxConfig, "", isPanicTxFunc)
HttpServer, err := evmrpc.NewEVMHTTPServer(infoLog, goodConfig, &MockClient{}, EVMKeeper, testApp.BaseApp, testApp.AnteHandler, ctxProvider, TxConfig, "", isPanicTxFunc)
if err != nil {
panic(err)
}
Expand All @@ -557,7 +557,7 @@ func init() {
badConfig := evmrpc.DefaultConfig
badConfig.HTTPPort = TestBadPort
badConfig.FilterTimeout = 500 * time.Millisecond
badHTTPServer, err := evmrpc.NewEVMHTTPServer(infoLog, badConfig, &MockBadClient{}, EVMKeeper, ctxProvider, TxConfig, "", nil)
badHTTPServer, err := evmrpc.NewEVMHTTPServer(infoLog, badConfig, &MockBadClient{}, EVMKeeper, testApp.BaseApp, testApp.AnteHandler, ctxProvider, TxConfig, "", nil)
if err != nil {
panic(err)
}
Expand All @@ -566,7 +566,7 @@ func init() {
}

// Start ws server
wsServer, err := evmrpc.NewEVMWebSocketServer(infoLog, goodConfig, &MockClient{}, EVMKeeper, ctxProvider, TxConfig, "")
wsServer, err := evmrpc.NewEVMWebSocketServer(infoLog, goodConfig, &MockClient{}, EVMKeeper, testApp.BaseApp, testApp.AnteHandler, ctxProvider, TxConfig, "")
if err != nil {
panic(err)
}
Expand Down
125 changes: 48 additions & 77 deletions evmrpc/simulate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ package evmrpc

import (
"context"
"crypto/sha256"
"errors"
"fmt"
"math/big"
"strings"
"time"

"github.com/sei-protocol/sei-chain/precompiles/wasmd"
"github.com/sei-protocol/sei-chain/utils/helpers"

"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -27,10 +28,10 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/sei-protocol/sei-chain/utils"
"github.com/sei-protocol/sei-chain/utils/metrics"
"github.com/sei-protocol/sei-chain/x/evm/keeper"
"github.com/sei-protocol/sei-chain/x/evm/state"
"github.com/sei-protocol/sei-chain/x/evm/types"
abci "github.com/tendermint/tendermint/abci/types"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/rpc/coretypes"
)
Expand All @@ -50,10 +51,12 @@ func NewSimulationAPI(
txDecoder sdk.TxDecoder,
tmClient rpcclient.Client,
config *SimulateConfig,
app *baseapp.BaseApp,
antehandler sdk.AnteHandler,
connectionType ConnectionType,
) *SimulationAPI {
return &SimulationAPI{
backend: NewBackend(ctxProvider, keeper, txDecoder, tmClient, config),
backend: NewBackend(ctxProvider, keeper, txDecoder, tmClient, config, app, antehandler),
connectionType: connectionType,
}
}
Expand Down Expand Up @@ -179,15 +182,19 @@ type Backend struct {
keeper *keeper.Keeper
tmClient rpcclient.Client
config *SimulateConfig
app *baseapp.BaseApp
antehandler sdk.AnteHandler
}

func NewBackend(ctxProvider func(int64) sdk.Context, keeper *keeper.Keeper, txDecoder sdk.TxDecoder, tmClient rpcclient.Client, config *SimulateConfig) *Backend {
func NewBackend(ctxProvider func(int64) sdk.Context, keeper *keeper.Keeper, txDecoder sdk.TxDecoder, tmClient rpcclient.Client, config *SimulateConfig, app *baseapp.BaseApp, antehandler sdk.AnteHandler) *Backend {
return &Backend{
ctxProvider: ctxProvider,
keeper: keeper,
txDecoder: txDecoder,
tmClient: tmClient,
config: config,
app: app,
antehandler: antehandler,
}
}

Expand Down Expand Up @@ -217,17 +224,9 @@ func (b *Backend) GetTransaction(ctx context.Context, txHash common.Hash) (tx *e
}
txIndex := hexutil.Uint(receipt.TransactionIndex)
tmTx := block.Block.Txs[int(txIndex)]
// We need to find the ethIndex
evmTxIndex, found := GetEvmTxIndex(block.Block.Txs, receipt.TransactionIndex, b.txDecoder, func(h common.Hash) bool {
_, err := b.keeper.GetReceipt(sdkCtx, h)
return err == nil
})
if !found {
return nil, common.Hash{}, 0, 0, errors.New("failed to find transaction in block")
}
tx = getEthTxForTxBz(tmTx, b.txDecoder)
blockHash = common.BytesToHash(block.Block.Header.Hash().Bytes())
return tx, blockHash, uint64(txHeight), uint64(evmTxIndex), nil
return tx, blockHash, uint64(txHeight), uint64(txIndex), nil
}

func (b *Backend) ChainDb() ethdb.Database {
Expand Down Expand Up @@ -311,82 +310,54 @@ func (b *Backend) StateAtTransaction(ctx context.Context, block *ethtypes.Block,
}
// get the parent block using block.parentHash
prevBlockHeight := block.Number().Int64() - 1
// Get statedb of parent block from the store
statedb := state.NewDBImpl(b.ctxProvider(prevBlockHeight).WithIsEVM(true), b.keeper, true)
if txIndex == 0 && len(block.Transactions()) == 0 {
return nil, vm.BlockContext{}, statedb, emptyRelease, nil
}
// Recompute transactions up to the target index. (only doing EVM at the moment, but should do both EVM + Cosmos)
signer := ethtypes.MakeSigner(b.ChainConfig(), block.Number(), block.Time())
for idx, tx := range block.Transactions() {
msg, err := core.TransactionToMessage(tx, signer, block.BaseFee())
if err != nil {
return nil, vm.BlockContext{}, nil, nil, err
}
txContext := core.NewEVMTxContext(msg)
blockContext, err := b.keeper.GetVMBlockContext(b.ctxProvider(prevBlockHeight), core.GasPool(b.RPCGasCap()))

blockNumber := block.Number().Int64()
tmBlock, err := b.tmClient.Block(ctx, &blockNumber)
if err != nil {
return nil, vm.BlockContext{}, nil, emptyRelease, fmt.Errorf("cannot find block %d from tendermint", blockNumber)
}
res, err := b.tmClient.Validators(ctx, &prevBlockHeight, nil, nil) // todo: load all
if err != nil {
return nil, vm.BlockContext{}, nil, emptyRelease, fmt.Errorf("failed to load validators for block %d from tendermint", prevBlockHeight)
}
reqBeginBlock := tmBlock.Block.ToReqBeginBlock(res.Validators)
sdkCtx := b.ctxProvider(prevBlockHeight)
_ = b.app.BeginBlock(sdkCtx, reqBeginBlock)
blockContext, err := b.keeper.GetVMBlockContext(sdkCtx, core.GasPool(b.RPCGasCap()))
if err != nil {
return nil, vm.BlockContext{}, nil, emptyRelease, err
}
for idx, tx := range tmBlock.Block.Txs {
sdkTx, err := b.txDecoder(tx)
if err != nil {
return nil, vm.BlockContext{}, nil, nil, err
panic(err)
}
// set block context time as of the block time (block time is the time of the CURRENT block)
blockContext.Time = block.Time()

// set address association for the sender if not present. Note that here we take the shortcut
// of querying from the latest height with the assumption that if this tx has been processed
// at all then its association must be present in the latest height
_, associated := b.keeper.GetSeiAddress(statedb.Ctx(), msg.From)
if !associated {
seiAddr, associatedNow := b.keeper.GetSeiAddress(b.ctxProvider(LatestCtxHeight), msg.From)
if !associatedNow {
err := types.NewAssociationMissingErr(msg.From.Hex())
metrics.IncrementAssociationError("state_at_tx", err)
return nil, vm.BlockContext{}, nil, nil, err
if idx == txIndex {
// Only run antehandlers as Geth will perform the actual execution
newCtx, err := b.antehandler(sdkCtx, sdkTx, false)
if err != nil {
return nil, vm.BlockContext{}, nil, emptyRelease, fmt.Errorf("transaction failed ante handler due to %s", err)
}
if err := helpers.NewAssociationHelper(b.keeper, b.keeper.BankKeeper(), b.keeper.AccountKeeper()).AssociateAddresses(statedb.Ctx(), seiAddr, msg.From, nil); err != nil {
return nil, vm.BlockContext{}, nil, nil, err
sdkCtx = newCtx
var evmMsg *types.MsgEVMTransaction
if msgs := sdkTx.GetMsgs(); len(msgs) != 1 {
return nil, vm.BlockContext{}, nil, emptyRelease, fmt.Errorf("cannot replay non-EVM transaction %d at block %d", idx, blockNumber)
} else if msg, ok := msgs[0].(*types.MsgEVMTransaction); !ok {
return nil, vm.BlockContext{}, nil, emptyRelease, fmt.Errorf("cannot replay non-EVM transaction %d at block %d", idx, blockNumber)
} else {
evmMsg = msg
}
ethTx, _ := evmMsg.AsTransaction()
return ethTx, *blockContext, state.NewDBImpl(sdkCtx.WithIsEVM(true), b.keeper, true), emptyRelease, nil
}

if idx == txIndex {
return tx, *blockContext, statedb, emptyRelease, nil
}
statedb.WithCtx(statedb.Ctx().WithEVMEntryViaWasmdPrecompile(wasmd.IsWasmdCall(tx.To())))
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(*blockContext, txContext, statedb, b.ChainConfig(), vm.Config{})
statedb.SetTxContext(tx.Hash(), idx)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
_ = b.app.DeliverTx(sdkCtx, abci.RequestDeliverTx{}, sdkTx, sha256.Sum256(tx))
}
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}

func (b *Backend) StateAtBlock(ctx context.Context, block *ethtypes.Block, reexec uint64, base vm.StateDB, readOnly bool, preferDisk bool) (vm.StateDB, tracers.StateReleaseFunc, error) {
emptyRelease := func() {}
statedb := state.NewDBImpl(b.ctxProvider(block.Number().Int64()-1), b.keeper, true)
signer := ethtypes.MakeSigner(b.ChainConfig(), block.Number(), block.Time())
for _, tx := range block.Transactions() {
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())

// set address association for the sender if not present. Note that here we take the shortcut
// of querying from the latest height with the assumption that if this tx has been processed
// at all then its association must be present in the latest height
_, associated := b.keeper.GetSeiAddress(statedb.Ctx(), msg.From)
if !associated {
seiAddr, associatedNow := b.keeper.GetSeiAddress(b.ctxProvider(LatestCtxHeight), msg.From)
if !associatedNow {
err := types.NewAssociationMissingErr(msg.From.Hex())
metrics.IncrementAssociationError("state_at_block", err)
continue // don't return error, just continue bc we want to process the rest of the txs and return the statedb
}
if err := helpers.NewAssociationHelper(b.keeper, b.keeper.BankKeeper(), b.keeper.AccountKeeper()).AssociateAddresses(statedb.Ctx(), seiAddr, msg.From, nil); err != nil {
return nil, emptyRelease, err
}
}
}
return statedb, emptyRelease, nil
}

Expand Down
10 changes: 7 additions & 3 deletions evmrpc/tracers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"time"

"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth/tracers"
Expand Down Expand Up @@ -36,8 +37,9 @@ type SeiDebugAPI struct {
*DebugAPI
}

func NewDebugAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txDecoder sdk.TxDecoder, config *SimulateConfig, connectionType ConnectionType) *DebugAPI {
backend := NewBackend(ctxProvider, k, txDecoder, tmClient, config)
func NewDebugAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txDecoder sdk.TxDecoder, config *SimulateConfig, app *baseapp.BaseApp,
antehandler sdk.AnteHandler, connectionType ConnectionType) *DebugAPI {
backend := NewBackend(ctxProvider, k, txDecoder, tmClient, config, app, antehandler)
tracersAPI := tracers.NewAPI(backend)
evictCallback := func(key common.Hash, value bool) {}
isPanicCache := expirable.NewLRU[common.Hash, bool](IsPanicCacheSize, evictCallback, IsPanicCacheTTL)
Expand All @@ -58,9 +60,11 @@ func NewSeiDebugAPI(
ctxProvider func(int64) sdk.Context,
txDecoder sdk.TxDecoder,
config *SimulateConfig,
app *baseapp.BaseApp,
antehandler sdk.AnteHandler,
connectionType ConnectionType,
) *SeiDebugAPI {
backend := NewBackend(ctxProvider, k, txDecoder, tmClient, config)
backend := NewBackend(ctxProvider, k, txDecoder, tmClient, config, app, antehandler)
tracersAPI := tracers.NewAPI(backend)
return &SeiDebugAPI{
DebugAPI: &DebugAPI{tracersAPI: tracersAPI, tmClient: tmClient, keeper: k, ctxProvider: ctxProvider, txDecoder: txDecoder, connectionType: connectionType},
Expand Down
Loading

0 comments on commit c520e0a

Please sign in to comment.