rpc: geth v1.10.9 changes (#624)

* rpc: geth v1.10.9 changes

* updates

* suggestGasTipCap

* update gRPC

* resend

* fixes

* rm unused func

* address TODO
This commit is contained in:
Federico Kunze Küllmer 2021-10-06 13:22:32 +02:00 committed by GitHub
parent 202bc5f1cd
commit bcdb982886
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 427 additions and 221 deletions

View File

@ -52,6 +52,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (evm) [tharsis#613](https://github.com/tharsis/ethermint/pull/613) Refactor `traceTx` * (evm) [tharsis#613](https://github.com/tharsis/ethermint/pull/613) Refactor `traceTx`
* (deps) [tharsis#610](https://github.com/tharsis/ethermint/pull/610) Bump Cosmos SDK to [v0.44.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.44.1). * (deps) [tharsis#610](https://github.com/tharsis/ethermint/pull/610) Bump Cosmos SDK to [v0.44.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.44.1).
### Features
* (rpc) [tharsis#624](https://github.com/tharsis/ethermint/pull/624) Implement new JSON-RPC endpoints from latest geth version
### Bug Fixes ### Bug Fixes
* (evm) [tharsis#616](https://github.com/tharsis/ethermint/issues/616) Fix halt on deeply nested stack of cache context. Stack is now flattened before iterating over the tx logs. * (evm) [tharsis#616](https://github.com/tharsis/ethermint/issues/616) Fix halt on deeply nested stack of cache context. Stack is now flattened before iterating over the tx logs.

File diff suppressed because one or more lines are too long

2
go.mod
View File

@ -34,7 +34,7 @@ require (
github.com/tyler-smith/go-bip39 v1.1.0 github.com/tyler-smith/go-bip39 v1.1.0
go.etcd.io/bbolt v1.3.6 // indirect go.etcd.io/bbolt v1.3.6 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
google.golang.org/genproto v0.0.0-20211001223012-bfb93cce50d9 google.golang.org/genproto v0.0.0-20211005153810-c76a74d43a8e
google.golang.org/grpc v1.41.0 google.golang.org/grpc v1.41.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0

4
go.sum
View File

@ -1759,8 +1759,8 @@ google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKr
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20211001223012-bfb93cce50d9 h1:eF1wcrhdz56Vugf8qNX5dD93ItkrhothojQyHXqloe0= google.golang.org/genproto v0.0.0-20211005153810-c76a74d43a8e h1:Im71rbA1N3CbIag/PumYhQcNR8bLNmuOtRIyOnnLsT8=
google.golang.org/genproto v0.0.0-20211001223012-bfb93cce50d9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211005153810-c76a74d43a8e/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=

View File

@ -42,8 +42,7 @@ message QueryBaseFeeRequest {}
// BaseFeeResponse returns the EIP1559 base fee. // BaseFeeResponse returns the EIP1559 base fee.
message QueryBaseFeeResponse { message QueryBaseFeeResponse {
string base_fee = 1 [ string base_fee = 1 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int"
(gogoproto.nullable) = false
]; ];
} }

View File

@ -41,29 +41,37 @@ import (
// Backend implements the functionality shared within namespaces. // Backend implements the functionality shared within namespaces.
// Implemented by EVMBackend. // Implemented by EVMBackend.
type Backend interface { type Backend interface {
// General Ethereum API
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
RPCMinGasPrice() int64
SuggestGasTipCap() (*big.Int, error)
// Blockchain API
BlockNumber() (hexutil.Uint64, error) BlockNumber() (hexutil.Uint64, error)
GetTendermintBlockByNumber(blockNum types.BlockNumber) (*tmrpctypes.ResultBlock, error)
GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map[string]interface{}, error) GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map[string]interface{}, error)
GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error)
CurrentHeader() *ethtypes.Header CurrentHeader() *ethtypes.Header
GetTendermintBlockByNumber(blockNum types.BlockNumber) (*tmrpctypes.ResultBlock, error)
HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, error) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, error)
HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error)
PendingTransactions() ([]*sdk.Tx, error) PendingTransactions() ([]*sdk.Tx, error)
GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error)
GetTransactionCount(address common.Address, blockNum types.BlockNumber) (*hexutil.Uint64, error) GetTransactionCount(address common.Address, blockNum types.BlockNumber) (*hexutil.Uint64, error)
SendTransaction(args evmtypes.TransactionArgs) (common.Hash, error) SendTransaction(args evmtypes.TransactionArgs) (common.Hash, error)
GetLogsByHeight(height *int64) ([][]*ethtypes.Log, error)
GetLogs(hash common.Hash) ([][]*ethtypes.Log, error)
BloomStatus() (uint64, uint64)
GetCoinbase() (sdk.AccAddress, error) GetCoinbase() (sdk.AccAddress, error)
GetTransactionByHash(txHash common.Hash) (*types.RPCTransaction, error) GetTransactionByHash(txHash common.Hash) (*types.RPCTransaction, error)
GetTxByEthHash(txHash common.Hash) (*tmrpctypes.ResultTx, error) GetTxByEthHash(txHash common.Hash) (*tmrpctypes.ResultTx, error)
EstimateGas(args evmtypes.TransactionArgs, blockNrOptional *types.BlockNumber) (hexutil.Uint64, error) EstimateGas(args evmtypes.TransactionArgs, blockNrOptional *types.BlockNumber) (hexutil.Uint64, error)
RPCGasCap() uint64 BaseFee() (*big.Int, error)
RPCMinGasPrice() int64
ChainConfig() *params.ChainConfig // Filter API
SuggestGasTipCap() (*big.Int, error) BloomStatus() (uint64, uint64)
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) GetFilteredBlocks(from int64, to int64, filter [][]filters.BloomIV, filterAddresses bool) ([]int64, error)
ChainConfig() *params.ChainConfig
SetTxDefaults(args evmtypes.TransactionArgs) (evmtypes.TransactionArgs, error)
} }
var _ Backend = (*EVMBackend)(nil) var _ Backend = (*EVMBackend)(nil)
@ -228,6 +236,14 @@ func (e *EVMBackend) EthBlockFromTendermint(
) (map[string]interface{}, error) { ) (map[string]interface{}, error) {
ethRPCTxs := []interface{}{} ethRPCTxs := []interface{}{}
ctx := types.ContextWithHeight(block.Height)
bfRes, err := e.queryClient.FeeMarket.BaseFee(ctx, &feemarkettypes.QueryBaseFeeRequest{})
if err != nil {
e.logger.Debug("failed to base fee", "height", block.Height, "error", err.Error())
return nil, err
}
for i, txBz := range block.Txs { for i, txBz := range block.Txs {
tx, err := e.clientCtx.TxConfig.TxDecoder()(txBz) tx, err := e.clientCtx.TxConfig.TxDecoder()(txBz)
if err != nil { if err != nil {
@ -237,43 +253,30 @@ func (e *EVMBackend) EthBlockFromTendermint(
for _, msg := range tx.GetMsgs() { for _, msg := range tx.GetMsgs() {
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx) ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
continue continue
} }
hash := ethMsg.AsTransaction().Hash() tx := ethMsg.AsTransaction()
if !fullTx { if !fullTx {
hash := tx.Hash()
ethRPCTxs = append(ethRPCTxs, hash) ethRPCTxs = append(ethRPCTxs, hash)
continue continue
} }
// get full transaction from message data rpcTx, err := types.NewRPCTransaction(
from, err := ethMsg.GetSender(e.chainID) tx,
if err != nil {
e.logger.Debug("failed to get sender from already included transaction", "hash", hash.Hex(), "error", err.Error())
from = common.HexToAddress(ethMsg.From)
}
txData, err := evmtypes.UnpackTxData(ethMsg.Data)
if err != nil {
e.logger.Debug("decoding failed", "error", err.Error())
return nil, fmt.Errorf("failed to unpack tx data: %w", err)
}
ethTx, err := types.NewTransactionFromData(
txData,
from,
hash,
common.BytesToHash(block.Hash()), common.BytesToHash(block.Hash()),
uint64(block.Height), uint64(block.Height),
uint64(i), uint64(i),
bfRes.BaseFee.BigInt(),
) )
if err != nil { if err != nil {
e.logger.Debug("NewTransactionFromData for receipt failed", "hash", hash.Hex(), "error", err.Error()) e.logger.Debug("NewTransactionFromData for receipt failed", "hash", tx.Hash().Hex(), "error", err.Error())
continue continue
} }
ethRPCTxs = append(ethRPCTxs, ethTx) ethRPCTxs = append(ethRPCTxs, rpcTx)
} }
} }
@ -286,8 +289,6 @@ func (e *EVMBackend) EthBlockFromTendermint(
ConsAddress: sdk.ConsAddress(block.Header.ProposerAddress).String(), ConsAddress: sdk.ConsAddress(block.Header.ProposerAddress).String(),
} }
ctx := types.ContextWithHeight(block.Height)
res, err := e.queryClient.ValidatorAccount(ctx, req) res, err := e.queryClient.ValidatorAccount(ctx, req)
if err != nil { if err != nil {
e.logger.Debug( e.logger.Debug(
@ -306,12 +307,6 @@ func (e *EVMBackend) EthBlockFromTendermint(
validatorAddr := common.BytesToAddress(addr) validatorAddr := common.BytesToAddress(addr)
bfRes, err := e.queryClient.FeeMarket.BaseFee(ctx, &feemarkettypes.QueryBaseFeeRequest{})
if err != nil {
e.logger.Debug("failed to base fee", "height", block.Height, "error", err.Error())
return nil, err
}
gasLimit, err := types.BlockMaxGasFromConsensusParams(ctx, e.clientCtx) gasLimit, err := types.BlockMaxGasFromConsensusParams(ctx, e.clientCtx)
if err != nil { if err != nil {
e.logger.Error("failed to query consensus params", "error", err.Error()) e.logger.Error("failed to query consensus params", "error", err.Error())
@ -376,7 +371,13 @@ func (e *EVMBackend) HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Heade
e.logger.Debug("HeaderByNumber BlockBloom failed", "height", resBlock.Block.Height) e.logger.Debug("HeaderByNumber BlockBloom failed", "height", resBlock.Block.Height)
} }
ethHeader := types.EthHeaderFromTendermint(resBlock.Block.Header) baseFee, err := e.BaseFee()
if err != nil {
e.logger.Debug("HeaderByNumber BaseFee failed", "height", resBlock.Block.Height, "error", err.Error())
return nil, err
}
ethHeader := types.EthHeaderFromTendermint(resBlock.Block.Header, baseFee)
ethHeader.Bloom = bloom ethHeader.Bloom = bloom
return ethHeader, nil return ethHeader, nil
} }
@ -398,7 +399,13 @@ func (e *EVMBackend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, erro
e.logger.Debug("HeaderByHash BlockBloom failed", "height", resBlock.Block.Height) e.logger.Debug("HeaderByHash BlockBloom failed", "height", resBlock.Block.Height)
} }
ethHeader := types.EthHeaderFromTendermint(resBlock.Block.Header) baseFee, err := e.BaseFee()
if err != nil {
e.logger.Debug("HeaderByHash BaseFee failed", "height", resBlock.Block.Height, "error", err.Error())
return nil, err
}
ethHeader := types.EthHeaderFromTendermint(resBlock.Block.Header, baseFee)
ethHeader.Bloom = bloom ethHeader.Bloom = bloom
return ethHeader, nil return ethHeader, nil
} }
@ -607,20 +614,24 @@ func (e *EVMBackend) SendTransaction(args evmtypes.TransactionArgs) (common.Hash
return common.Hash{}, fmt.Errorf("%s; %s", keystore.ErrNoMatch, err.Error()) return common.Hash{}, fmt.Errorf("%s; %s", keystore.ErrNoMatch, err.Error())
} }
args, err = e.setTxDefaults(args) args, err = e.SetTxDefaults(args)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
msg := args.ToTransaction() msg := args.ToTransaction()
if err := msg.ValidateBasic(); err != nil { if err := msg.ValidateBasic(); err != nil {
e.logger.Debug("tx failed basic validation", "error", err.Error()) e.logger.Debug("tx failed basic validation", "error", err.Error())
return common.Hash{}, err return common.Hash{}, err
} }
// TODO: get from chain config bn, err := e.BlockNumber()
signer := ethtypes.LatestSignerForChainID(args.ChainID.ToInt()) if err != nil {
e.logger.Debug("failed to fetch latest block number", "error", err.Error())
return common.Hash{}, err
}
signer := ethtypes.MakeSigner(e.ChainConfig(), new(big.Int).SetUint64(uint64(bn)))
// Sign transaction // Sign transaction
if err := msg.Sign(signer, e.clientCtx.Keyring); err != nil { if err := msg.Sign(signer, e.clientCtx.Keyring); err != nil {
@ -641,8 +652,7 @@ func (e *EVMBackend) SendTransaction(args evmtypes.TransactionArgs) (common.Hash
} }
builder.SetExtensionOptions(option) builder.SetExtensionOptions(option)
err = builder.SetMsgs(msg) if err = builder.SetMsgs(msg); err != nil {
if err != nil {
e.logger.Error("builder.SetMsgs failed", "error", err.Error()) e.logger.Error("builder.SetMsgs failed", "error", err.Error())
} }
@ -701,7 +711,22 @@ func (e *EVMBackend) EstimateGas(args evmtypes.TransactionArgs, blockNrOptional
return 0, err return 0, err
} }
req := evmtypes.EthCallRequest{Args: bz, GasCap: e.RPCGasCap()} baseFee, err := e.BaseFee()
if err != nil {
return 0, err
}
var bf *sdk.Int
if baseFee != nil {
aux := sdk.NewIntFromBigInt(baseFee)
bf = &aux
}
req := evmtypes.EthCallRequest{
Args: bz,
GasCap: e.RPCGasCap(),
BaseFee: bf,
}
// From ContextWithHeight: if the provided height is 0, // From ContextWithHeight: if the provided height is 0,
// it will return an empty context and the gRPC query will use // it will return an empty context and the gRPC query will use
@ -745,7 +770,7 @@ func (e *EVMBackend) RPCGasCap() uint64 {
// the node config. If set value is 0, it will default to 20. // the node config. If set value is 0, it will default to 20.
func (e *EVMBackend) RPCMinGasPrice() int64 { func (e *EVMBackend) RPCMinGasPrice() int64 {
evmParams, err := e.queryClient.Params(context.Background(), &evmtypes.QueryParamsRequest{}) evmParams, err := e.queryClient.Params(e.ctx, &evmtypes.QueryParamsRequest{})
if err != nil { if err != nil {
return ethermint.DefaultGasPrice return ethermint.DefaultGasPrice
} }
@ -771,8 +796,29 @@ func (e *EVMBackend) ChainConfig() *params.ChainConfig {
// SuggestGasTipCap returns the suggested tip cap // SuggestGasTipCap returns the suggested tip cap
func (e *EVMBackend) SuggestGasTipCap() (*big.Int, error) { func (e *EVMBackend) SuggestGasTipCap() (*big.Int, error) {
// TODO: implement out := new(big.Int).SetInt64(e.RPCMinGasPrice())
return big.NewInt(1), nil return out, nil
}
// BaseFee returns the base fee tracked by the Fee Market module. If the base fee is not enabled,
// it returns the initial base fee amount.
func (e *EVMBackend) BaseFee() (*big.Int, error) {
res, err := e.queryClient.FeeMarket.BaseFee(e.ctx, &feemarkettypes.QueryBaseFeeRequest{})
if err != nil {
return nil, err
}
if res.BaseFee != nil {
return res.BaseFee.BigInt(), nil
}
resParams, err := e.queryClient.FeeMarket.Params(e.ctx, &feemarkettypes.QueryParamsRequest{})
if err != nil {
return nil, err
}
baseFee := big.NewInt(resParams.Params.InitialBaseFee)
return baseFee, nil
} }
// GetFilteredBlocks returns the block height list match the given bloom filters. // GetFilteredBlocks returns the block height list match the given bloom filters.

View File

@ -19,9 +19,9 @@ import (
evmtypes "github.com/tharsis/ethermint/x/evm/types" evmtypes "github.com/tharsis/ethermint/x/evm/types"
) )
// setTxDefaults populates tx message with default values in case they are not // SetTxDefaults populates tx message with default values in case they are not
// provided on the args // provided on the args
func (e *EVMBackend) setTxDefaults(args evmtypes.TransactionArgs) (evmtypes.TransactionArgs, error) { func (e *EVMBackend) SetTxDefaults(args evmtypes.TransactionArgs) (evmtypes.TransactionArgs, error) {
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return args, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") return args, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
} }

View File

@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/rpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -45,6 +46,7 @@ type PublicAPI struct {
logger log.Logger logger log.Logger
backend backend.Backend backend backend.Backend
nonceLock *rpctypes.AddrLocker nonceLock *rpctypes.AddrLocker
signer ethtypes.Signer
} }
// NewPublicAPI creates an instance of the public ETH Web3 API. // NewPublicAPI creates an instance of the public ETH Web3 API.
@ -76,6 +78,10 @@ func NewPublicAPI(
clientCtx = clientCtx.WithKeyring(kr) clientCtx = clientCtx.WithKeyring(kr)
} }
// The signer used by the API should always be the 'latest' known one because we expect
// signers to be backwards-compatible with old transactions.
signer := ethtypes.LatestSigner(backend.ChainConfig())
api := &PublicAPI{ api := &PublicAPI{
ctx: context.Background(), ctx: context.Background(),
clientCtx: clientCtx, clientCtx: clientCtx,
@ -84,6 +90,7 @@ func NewPublicAPI(
logger: logger.With("client", "json-rpc"), logger: logger.With("client", "json-rpc"),
backend: backend, backend: backend,
nonceLock: nonceLock, nonceLock: nonceLock,
signer: signer,
} }
return api return api
@ -108,14 +115,30 @@ func (e *PublicAPI) ProtocolVersion() hexutil.Uint {
return hexutil.Uint(ethermint.ProtocolVersion) return hexutil.Uint(ethermint.ProtocolVersion)
} }
// ChainId returns the chain's identifier in hex format // ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config.
func (e *PublicAPI) ChainId() (hexutil.Uint, error) { // nolint func (e *PublicAPI) ChainId() (*hexutil.Big, error) { // nolint
e.logger.Debug("eth_chainId") e.logger.Debug("eth_chainId")
return hexutil.Uint(uint(e.chainIDEpoch.Uint64())), nil // if current block is at or past the EIP-155 replay-protection fork block, return chainID from config
bn, err := e.backend.BlockNumber()
if err != nil {
e.logger.Debug("failed to fetch latest block number", "error", err.Error())
return (*hexutil.Big)(e.chainIDEpoch), nil
}
if config := e.backend.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")
} }
// Syncing returns whether or not the current node is syncing with other peers. Returns false if not, or a struct // Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not
// outlining the state of the sync if it is. // yet received the latest block headers from its pears. In case it is synchronizing:
// - startingBlock: block number this node started to synchronize from
// - currentBlock: block number this node is currently importing
// - highestBlock: block number of the highest block header this node has received from peers
// - pulledStates: number of state entries processed until now
// - knownStates: number of known state entries that still need to be pulled
func (e *PublicAPI) Syncing() (interface{}, error) { func (e *PublicAPI) Syncing() (interface{}, error) {
e.logger.Debug("eth_syncing") e.logger.Debug("eth_syncing")
@ -129,8 +152,8 @@ func (e *PublicAPI) Syncing() (interface{}, error) {
} }
return map[string]interface{}{ return map[string]interface{}{
// "startingBlock": nil, // NA "startingBlock": hexutil.Uint64(status.SyncInfo.EarliestBlockHeight),
"currentBlock": hexutil.Uint64(status.SyncInfo.LatestBlockHeight), "currentBlock": hexutil.Uint64(status.SyncInfo.LatestBlockHeight),
// "highestBlock": nil, // NA // "highestBlock": nil, // NA
// "pulledStates": nil, // NA // "pulledStates": nil, // NA
// "knownStates": nil, // NA // "knownStates": nil, // NA
@ -162,10 +185,35 @@ func (e *PublicAPI) Hashrate() hexutil.Uint64 {
} }
// GasPrice returns the current gas price based on Ethermint's gas price oracle. // GasPrice returns the current gas price based on Ethermint's gas price oracle.
func (e *PublicAPI) GasPrice() *hexutil.Big { func (e *PublicAPI) GasPrice() (*hexutil.Big, error) {
e.logger.Debug("eth_gasPrice") e.logger.Debug("eth_gasPrice")
out := new(big.Int).SetInt64(e.backend.RPCMinGasPrice()) tipcap, err := e.backend.SuggestGasTipCap()
return (*hexutil.Big)(out) if err != nil {
return nil, err
}
if head := e.backend.CurrentHeader(); head.BaseFee != nil {
tipcap.Add(tipcap, head.BaseFee)
}
return (*hexutil.Big)(tipcap), nil
}
// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic fee transactions.
func (e *PublicAPI) MaxPriorityFeePerGas() (*hexutil.Big, error) {
e.logger.Debug("eth_maxPriorityFeePerGas")
tipcap, err := e.backend.SuggestGasTipCap()
if err != nil {
return nil, err
}
return (*hexutil.Big)(tipcap), nil
}
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")
} }
// Accounts returns the list of accounts available to this node. // Accounts returns the list of accounts available to this node.
@ -355,6 +403,29 @@ func (e *PublicAPI) SendTransaction(args evmtypes.TransactionArgs) (common.Hash,
return e.backend.SendTransaction(args) return e.backend.SendTransaction(args)
} }
// FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields)
// on a given unsigned transaction, and returns it to the caller for further
// processing (signing + broadcast).
func (e *PublicAPI) FillTransaction(args evmtypes.TransactionArgs) (*rpctypes.SignTransactionResult, error) {
// Set some sanity defaults and terminate on failure
args, err := e.backend.SetTxDefaults(args)
if err != nil {
return nil, err
}
// Assemble the transaction and obtain rlp
tx := args.ToTransaction().AsTransaction()
data, err := tx.MarshalBinary()
if err != nil {
return nil, err
}
return &rpctypes.SignTransactionResult{
Raw: data,
Tx: tx,
}, nil
}
// SendRawTransaction send a raw Ethereum transaction. // SendRawTransaction send a raw Ethereum transaction.
func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) { func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) {
e.logger.Debug("eth_sendRawTransaction", "length", len(data)) e.logger.Debug("eth_sendRawTransaction", "length", len(data))
@ -407,7 +478,12 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
return common.Hash{}, err return common.Hash{}, err
} }
fees := sdk.Coins{sdk.NewCoin(res.Params.EvmDenom, sdk.NewIntFromBigInt(txData.Fee()))} fees := sdk.Coins{
{
Denom: res.Params.EvmDenom,
Amount: sdk.NewIntFromBigInt(txData.Fee()),
},
}
builder.SetFeeAmount(fees) builder.SetFeeAmount(fees)
builder.SetGasLimit(ethereumTx.GetGas()) builder.SetGasLimit(ethereumTx.GetGas())
@ -433,6 +509,57 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
return txHash, nil return txHash, nil
} }
// Resend accepts an existing transaction and a new gas price and limit. It will remove
// the given transaction from the pool and reinsert it with the new gas price and limit.
func (e *PublicAPI) Resend(ctx context.Context, args evmtypes.TransactionArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) {
e.logger.Debug("eth_resend", "args", args.String())
if args.Nonce == nil {
return common.Hash{}, fmt.Errorf("missing transaction nonce in transaction spec")
}
args, err := e.backend.SetTxDefaults(args)
if err != nil {
return common.Hash{}, err
}
matchTx := args.ToTransaction().AsTransaction()
pending, err := e.backend.PendingTransactions()
if err != nil {
return common.Hash{}, err
}
for _, tx := range pending {
p, err := evmtypes.UnwrapEthereumMsg(tx)
if err != nil {
// not valid ethereum tx
continue
}
pTx := p.AsTransaction()
wantSigHash := e.signer.Hash(matchTx)
pFrom, err := ethtypes.Sender(e.signer, pTx)
if err != nil {
continue
}
if pFrom == *args.From && e.signer.Hash(pTx) == wantSigHash {
// Match. Re-sign and send the transaction.
if gasPrice != nil && (*big.Int)(gasPrice).Sign() != 0 {
args.GasPrice = gasPrice
}
if gasLimit != nil && *gasLimit != 0 {
args.Gas = gasLimit
}
return e.backend.SendTransaction(args) // TODO: this calls SetTxDefaults again, refactor to avoid calling it twice
}
}
return common.Hash{}, fmt.Errorf("transaction %#x not found", matchTx.Hash())
}
// Call performs a raw contract call. // Call performs a raw contract call.
func (e *PublicAPI) Call(args evmtypes.TransactionArgs, blockNrOrHash rpctypes.BlockNumberOrHash, _ *rpctypes.StateOverride) (hexutil.Bytes, error) { func (e *PublicAPI) Call(args evmtypes.TransactionArgs, blockNrOrHash rpctypes.BlockNumberOrHash, _ *rpctypes.StateOverride) (hexutil.Bytes, error) {
e.logger.Debug("eth_call", "args", args.String(), "block number or hash", blockNrOrHash) e.logger.Debug("eth_call", "args", args.String(), "block number or hash", blockNrOrHash)
@ -458,7 +585,23 @@ func (e *PublicAPI) doCall(
if err != nil { if err != nil {
return nil, err return nil, err
} }
req := evmtypes.EthCallRequest{Args: bz, GasCap: e.backend.RPCGasCap()}
baseFee, err := e.backend.BaseFee()
if err != nil {
return nil, err
}
var bf *sdk.Int
if baseFee != nil {
aux := sdk.NewIntFromBigInt(baseFee)
bf = &aux
}
req := evmtypes.EthCallRequest{
Args: bz,
GasCap: e.backend.RPCGasCap(),
BaseFee: bf,
}
// From ContextWithHeight: if the provided height is 0, // From ContextWithHeight: if the provided height is 0,
// it will return an empty context and the gRPC query will use // it will return an empty context and the gRPC query will use
@ -536,12 +679,17 @@ func (e *PublicAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexu
return nil, err return nil, err
} }
baseFee, err := e.backend.BaseFee()
if err != nil {
return nil, err
}
return rpctypes.NewTransactionFromMsg( return rpctypes.NewTransactionFromMsg(
msg, msg,
hash, hash,
uint64(resBlock.Block.Height), uint64(resBlock.Block.Height),
uint64(idx), uint64(idx),
e.chainIDEpoch, baseFee,
) )
} }

View File

@ -3,6 +3,7 @@ package filters
import ( import (
"context" "context"
"fmt" "fmt"
"math/big"
"sync" "sync"
"time" "time"
@ -17,6 +18,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
evmtypes "github.com/tharsis/ethermint/x/evm/types" evmtypes "github.com/tharsis/ethermint/x/evm/types"
@ -223,6 +225,9 @@ func (api *PublicFilterAPI) NewBlockFilter() rpc.ID {
return rpc.ID(fmt.Sprintf("error creating block filter: %s", err.Error())) return rpc.ID(fmt.Sprintf("error creating block filter: %s", err.Error()))
} }
// TODO: use events to get the base fee amount
baseFee := big.NewInt(params.InitialBaseFee)
api.filtersMu.Lock() api.filtersMu.Lock()
api.filters[headerSub.ID()] = &filter{typ: filters.BlocksSubscription, deadline: time.NewTimer(deadline), hashes: []common.Hash{}, s: headerSub} api.filters[headerSub.ID()] = &filter{typ: filters.BlocksSubscription, deadline: time.NewTimer(deadline), hashes: []common.Hash{}, s: headerSub}
api.filtersMu.Unlock() api.filtersMu.Unlock()
@ -246,7 +251,7 @@ func (api *PublicFilterAPI) NewBlockFilter() rpc.ID {
continue continue
} }
header := types.EthHeaderFromTendermint(data.Header) header := types.EthHeaderFromTendermint(data.Header, baseFee)
api.filtersMu.Lock() api.filtersMu.Lock()
if f, found := api.filters[headerSub.ID()]; found { if f, found := api.filters[headerSub.ID()]; found {
f.hashes = append(f.hashes, header.Hash()) f.hashes = append(f.hashes, header.Hash())
@ -279,6 +284,9 @@ func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, er
return &rpc.Subscription{}, err return &rpc.Subscription{}, err
} }
// TODO: use events to get the base fee amount
baseFee := big.NewInt(params.InitialBaseFee)
go func(headersCh <-chan coretypes.ResultEvent) { go func(headersCh <-chan coretypes.ResultEvent) {
defer cancelSubs() defer cancelSubs()
@ -296,7 +304,7 @@ func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, er
continue continue
} }
header := types.EthHeaderFromTendermint(data.Header) header := types.EthHeaderFromTendermint(data.Header, baseFee)
err = notifier.Notify(rpcSub.ID, header) err = notifier.Notify(rpcSub.ID, header)
if err != nil { if err != nil {
headersSub.err <- err headersSub.err <- err

View File

@ -150,7 +150,7 @@ func (api *PrivateAccountAPI) UnlockAccount(_ context.Context, addr common.Addre
// SendTransaction will create a transaction from the given arguments and // SendTransaction will create a transaction from the given arguments and
// tries to sign it with the key associated with args.To. If the given password isn't // tries to sign it with the key associated with args.To. If the given password isn't
// able to decrypt the key it fails. // able to decrypt the key it fails.
func (api *PrivateAccountAPI) SendTransaction(_ context.Context, args evmtypes.TransactionArgs, pwrd string) (common.Hash, error) { func (api *PrivateAccountAPI) SendTransaction(_ context.Context, args evmtypes.TransactionArgs, _ string) (common.Hash, error) {
api.logger.Debug("personal_sendTransaction", "address", args.To.String()) api.logger.Debug("personal_sendTransaction", "address", args.To.String())
if args.From == nil { if args.From == nil {
@ -177,7 +177,7 @@ func (api *PrivateAccountAPI) SendTransaction(_ context.Context, args evmtypes.T
// The key used to calculate the signature is decrypted with the given password. // The key used to calculate the signature is decrypted with the given password.
// //
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
func (api *PrivateAccountAPI) Sign(_ context.Context, data hexutil.Bytes, addr common.Address, pwrd string) (hexutil.Bytes, error) { func (api *PrivateAccountAPI) Sign(_ context.Context, data hexutil.Bytes, addr common.Address, _ string) (hexutil.Bytes, error) {
api.logger.Debug("personal_sign", "data", data, "address", addr.String()) api.logger.Debug("personal_sign", "data", data, "address", addr.String())
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())

View File

@ -66,3 +66,16 @@ type OverrideAccount struct {
State *map[common.Hash]common.Hash `json:"state"` State *map[common.Hash]common.Hash `json:"state"`
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
} }
type FeeHistoryResult struct {
OldestBlock *hexutil.Big `json:"oldestBlock"`
Reward [][]*hexutil.Big `json:"reward,omitempty"`
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
GasUsedRatio []float64 `json:"gasUsedRatio"`
}
// SignTransactionResult represents a RLP encoded signed transaction.
type SignTransactionResult struct {
Raw hexutil.Bytes `json:"raw"`
Tx *ethtypes.Transaction `json:"tx"`
}

View File

@ -16,6 +16,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
) )
@ -33,56 +34,14 @@ func RawTxToEthTx(clientCtx client.Context, txBz tmtypes.Tx) (*evmtypes.MsgEther
return ethTx, nil return ethTx, nil
} }
// NewTransaction returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available).
func NewTransaction(tx *ethtypes.Transaction, blockHash common.Hash, blockNumber, index uint64) *RPCTransaction {
// Determine the signer. For replay-protected transactions, use the most permissive
// signer, because we assume that signers are backwards-compatible with old
// transactions. For non-protected transactions, the homestead signer signer is used
// because the return value of ChainId is zero for those transactions.
var signer ethtypes.Signer
if tx.Protected() {
signer = ethtypes.LatestSignerForChainID(tx.ChainId())
} else {
signer = ethtypes.HomesteadSigner{}
}
from, _ := ethtypes.Sender(signer, tx)
v, r, s := tx.RawSignatureValues()
result := &RPCTransaction{
Type: hexutil.Uint64(tx.Type()),
From: from,
Gas: hexutil.Uint64(tx.Gas()),
GasPrice: (*hexutil.Big)(tx.GasPrice()),
Hash: tx.Hash(), // NOTE: transaction hash here uses the ethereum format for compatibility
Input: hexutil.Bytes(tx.Data()),
Nonce: hexutil.Uint64(tx.Nonce()),
To: tx.To(),
Value: (*hexutil.Big)(tx.Value()),
V: (*hexutil.Big)(v),
R: (*hexutil.Big)(r),
S: (*hexutil.Big)(s),
}
if blockHash != (common.Hash{}) {
result.BlockHash = &blockHash
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
result.TransactionIndex = (*hexutil.Uint64)(&index)
}
if tx.Type() == ethtypes.AccessListTxType {
al := tx.AccessList()
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
}
return result
}
// EthHeaderFromTendermint is an util function that returns an Ethereum Header // EthHeaderFromTendermint is an util function that returns an Ethereum Header
// from a tendermint Header. // from a tendermint Header.
func EthHeaderFromTendermint(header tmtypes.Header) *ethtypes.Header { func EthHeaderFromTendermint(header tmtypes.Header, baseFee *big.Int) *ethtypes.Header {
txHash := ethtypes.EmptyRootHash txHash := ethtypes.EmptyRootHash
if len(header.DataHash) == 0 { if len(header.DataHash) == 0 {
txHash = common.BytesToHash(header.DataHash) txHash = common.BytesToHash(header.DataHash)
} }
return &ethtypes.Header{ return &ethtypes.Header{
ParentHash: common.BytesToHash(header.LastBlockID.Hash.Bytes()), ParentHash: common.BytesToHash(header.LastBlockID.Hash.Bytes()),
UncleHash: ethtypes.EmptyUncleHash, UncleHash: ethtypes.EmptyUncleHash,
@ -99,6 +58,7 @@ func EthHeaderFromTendermint(header tmtypes.Header) *ethtypes.Header {
Extra: nil, Extra: nil,
MixDigest: common.Hash{}, MixDigest: common.Hash{},
Nonce: ethtypes.BlockNonce{}, Nonce: ethtypes.BlockNonce{},
BaseFee: baseFee,
} }
} }
@ -220,65 +180,67 @@ func NewTransactionFromMsg(
msg *evmtypes.MsgEthereumTx, msg *evmtypes.MsgEthereumTx,
blockHash common.Hash, blockHash common.Hash,
blockNumber, index uint64, blockNumber, index uint64,
chainID *big.Int, baseFee *big.Int,
) (*RPCTransaction, error) { ) (*RPCTransaction, error) {
from, err := msg.GetSender(chainID) tx := msg.AsTransaction()
if err != nil { return NewRPCTransaction(tx, blockHash, blockNumber, index, baseFee)
return nil, err
}
data, err := evmtypes.UnpackTxData(msg.Data)
if err != nil {
return nil, fmt.Errorf("failed to unpack tx data: %w", err)
}
return NewTransactionFromData(data, from, msg.AsTransaction().Hash(), blockHash, blockNumber, index)
} }
// NewTransactionFromData returns a transaction that will serialize to the RPC // NewTransactionFromData returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available). // representation, with the given location metadata set (if available).
func NewTransactionFromData( func NewRPCTransaction(
txData evmtypes.TxData, tx *ethtypes.Transaction, blockHash common.Hash, blockNumber, index uint64, baseFee *big.Int,
from common.Address,
txHash, blockHash common.Hash,
blockNumber, index uint64,
) (*RPCTransaction, error) { ) (*RPCTransaction, error) {
if txHash == (common.Hash{}) { // Determine the signer. For replay-protected transactions, use the most permissive
txHash = ethtypes.EmptyRootHash // signer, because we assume that signers are backwards-compatible with old
// transactions. For non-protected transactions, the homestead signer signer is used
// because the return value of ChainId is zero for those transactions.
var signer ethtypes.Signer
if tx.Protected() {
signer = ethtypes.LatestSignerForChainID(tx.ChainId())
} else {
signer = ethtypes.HomesteadSigner{}
} }
from, _ := ethtypes.Sender(signer, tx)
v, r, s := txData.GetRawSignatureValues() v, r, s := tx.RawSignatureValues()
result := &RPCTransaction{
rpcTx := &RPCTransaction{ Type: hexutil.Uint64(tx.Type()),
Type: hexutil.Uint64(txData.TxType()),
From: from, From: from,
Gas: hexutil.Uint64(txData.GetGas()), Gas: hexutil.Uint64(tx.Gas()),
GasPrice: (*hexutil.Big)(txData.GetGasPrice()), GasPrice: (*hexutil.Big)(tx.GasPrice()),
Hash: txHash, Hash: tx.Hash(),
Input: hexutil.Bytes(txData.GetData()), Input: hexutil.Bytes(tx.Data()),
Nonce: hexutil.Uint64(txData.GetNonce()), Nonce: hexutil.Uint64(tx.Nonce()),
To: txData.GetTo(), To: tx.To(),
Value: (*hexutil.Big)(txData.GetValue()), Value: (*hexutil.Big)(tx.Value()),
V: (*hexutil.Big)(v), V: (*hexutil.Big)(v),
R: (*hexutil.Big)(r), R: (*hexutil.Big)(r),
S: (*hexutil.Big)(s), S: (*hexutil.Big)(s),
} }
if rpcTx.To == nil {
addr := common.Address{}
rpcTx.To = &addr
}
if blockHash != (common.Hash{}) { if blockHash != (common.Hash{}) {
rpcTx.BlockHash = &blockHash result.BlockHash = &blockHash
rpcTx.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
rpcTx.TransactionIndex = (*hexutil.Uint64)(&index) result.TransactionIndex = (*hexutil.Uint64)(&index)
} }
switch tx.Type() {
if txData.TxType() == ethtypes.AccessListTxType { case ethtypes.AccessListTxType:
accesses := txData.GetAccessList() al := tx.AccessList()
rpcTx.Accesses = &accesses result.Accesses = &al
rpcTx.ChainID = (*hexutil.Big)(txData.GetChainID()) result.ChainID = (*hexutil.Big)(tx.ChainId())
case ethtypes.DynamicFeeTxType:
al := tx.AccessList()
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
// if the transaction has been mined, compute the effective gas price
if baseFee != nil && blockHash != (common.Hash{}) {
// price = min(tip, gasFeeCap - baseFee) + baseFee
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
result.GasPrice = (*hexutil.Big)(price)
} else {
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
}
} }
return result, nil
return rpcTx, nil
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
@ -352,6 +353,9 @@ func (api *pubSubAPI) subscribeNewHeads(wsConn *wsConn) (rpc.ID, error) {
return "", errors.Wrap(err, "error creating block filter") return "", errors.Wrap(err, "error creating block filter")
} }
// TODO: use events
baseFee := big.NewInt(params.InitialBaseFee)
unsubscribed := make(chan struct{}) unsubscribed := make(chan struct{})
api.filtersMu.Lock() api.filtersMu.Lock()
api.filters[subID] = &wsSubscription{ api.filters[subID] = &wsSubscription{
@ -377,7 +381,7 @@ func (api *pubSubAPI) subscribeNewHeads(wsConn *wsConn) (rpc.ID, error) {
continue continue
} }
header := types.EthHeaderFromTendermint(data.Header) header := types.EthHeaderFromTendermint(data.Header, baseFee)
api.filtersMu.RLock() api.filtersMu.RLock()
for subID, wsSub := range api.filters { for subID, wsSub := range api.filters {

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"math/big"
"net/http" "net/http"
"strings" "strings"
@ -15,6 +16,7 @@ import (
authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
rpctypes "github.com/tharsis/ethermint/rpc/ethereum/types" rpctypes "github.com/tharsis/ethermint/rpc/ethereum/types"
feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
@ -79,6 +81,17 @@ func getEthTransactionByHash(clientCtx client.Context, hashHex string) ([]byte,
return nil, err return nil, err
} }
client := feemarkettypes.NewQueryClient(clientCtx)
res, err := client.BaseFee(context.Background(), &feemarkettypes.QueryBaseFeeRequest{})
if err != nil {
return nil, err
}
var baseFee *big.Int
if res.BaseFee != nil {
baseFee = res.BaseFee.BigInt()
}
blockHash := common.BytesToHash(block.Block.Header.Hash()) blockHash := common.BytesToHash(block.Block.Header.Hash())
ethTx, err := rpctypes.RawTxToEthTx(clientCtx, tx.Tx) ethTx, err := rpctypes.RawTxToEthTx(clientCtx, tx.Tx)
@ -87,7 +100,11 @@ func getEthTransactionByHash(clientCtx client.Context, hashHex string) ([]byte,
} }
height := uint64(tx.Height) height := uint64(tx.Height)
rpcTx := rpctypes.NewTransaction(ethTx.AsTransaction(), blockHash, height, uint64(tx.Index))
rpcTx, err := rpctypes.NewRPCTransaction(ethTx.AsTransaction(), blockHash, height, uint64(tx.Index), baseFee)
if err != nil {
return nil, err
}
return json.Marshal(rpcTx) return json.Marshal(rpcTx)
} }

View File

@ -113,7 +113,7 @@ func (args *TransactionArgs) ToTransaction() *MsgEthereumTx {
GasFeeCap: &maxFeePerGas, GasFeeCap: &maxFeePerGas,
GasTipCap: &maxPriorityFeePerGas, GasTipCap: &maxPriorityFeePerGas,
Amount: &value, Amount: &value,
Data: args.data(), Data: args.GetData(),
Accesses: al, Accesses: al,
} }
case args.AccessList != nil: case args.AccessList != nil:
@ -124,7 +124,7 @@ func (args *TransactionArgs) ToTransaction() *MsgEthereumTx {
GasLimit: gas, GasLimit: gas,
GasPrice: &gasPrice, GasPrice: &gasPrice,
Amount: &value, Amount: &value,
Data: args.data(), Data: args.GetData(),
Accesses: NewAccessList(args.AccessList), Accesses: NewAccessList(args.AccessList),
} }
default: default:
@ -134,7 +134,7 @@ func (args *TransactionArgs) ToTransaction() *MsgEthereumTx {
GasLimit: gas, GasLimit: gas,
GasPrice: &gasPrice, GasPrice: &gasPrice,
Amount: &value, Amount: &value,
Data: args.data(), Data: args.GetData(),
} }
} }
@ -162,7 +162,7 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (e
} }
// Set sender address or use zero address if none specified. // Set sender address or use zero address if none specified.
addr := args.from() addr := args.GetFrom()
// Set default gas & gas price if none were set // Set default gas & gas price if none were set
gas := globalGasCap gas := globalGasCap
@ -214,7 +214,7 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (e
if args.Value != nil { if args.Value != nil {
value = args.Value.ToInt() value = args.Value.ToInt()
} }
data := args.data() data := args.GetData()
var accessList ethtypes.AccessList var accessList ethtypes.AccessList
if args.AccessList != nil { if args.AccessList != nil {
accessList = *args.AccessList accessList = *args.AccessList
@ -223,16 +223,16 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (e
return msg, nil return msg, nil
} }
// from retrieves the transaction sender address. // GetFrom retrieves the transaction sender address.
func (args *TransactionArgs) from() common.Address { func (args *TransactionArgs) GetFrom() common.Address {
if args.From == nil { if args.From == nil {
return common.Address{} return common.Address{}
} }
return *args.From return *args.From
} }
// data retrieves the transaction calldata. Input field is preferred. // GetData retrieves the transaction calldata. Input field is preferred.
func (args *TransactionArgs) data() []byte { func (args *TransactionArgs) GetData() []byte {
if args.Input != nil { if args.Input != nil {
return *args.Input return *args.Input
} }

View File

@ -2,7 +2,6 @@ package keeper
import ( import (
"context" "context"
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -25,15 +24,15 @@ func (k Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.Q
func (k Keeper) BaseFee(c context.Context, _ *types.QueryBaseFeeRequest) (*types.QueryBaseFeeResponse, error) { func (k Keeper) BaseFee(c context.Context, _ *types.QueryBaseFeeRequest) (*types.QueryBaseFeeResponse, error) {
ctx := sdk.UnwrapSDKContext(c) ctx := sdk.UnwrapSDKContext(c)
res := &types.QueryBaseFeeResponse{}
baseFee := k.GetBaseFee(ctx) baseFee := k.GetBaseFee(ctx)
if baseFee == nil {
// TODO: should this be 0? 1? error? if baseFee != nil {
baseFee = big.NewInt(0) aux := sdk.NewIntFromBigInt(baseFee)
res.BaseFee = &aux
} }
return &types.QueryBaseFeeResponse{ return res, nil
BaseFee: sdk.NewIntFromBigInt(baseFee),
}, nil
} }
// BlockGas implements the Query/BlockGas gRPC method // BlockGas implements the Query/BlockGas gRPC method

View File

@ -153,7 +153,7 @@ var xxx_messageInfo_QueryBaseFeeRequest proto.InternalMessageInfo
// BaseFeeResponse returns the EIP1559 base fee. // BaseFeeResponse returns the EIP1559 base fee.
type QueryBaseFeeResponse struct { type QueryBaseFeeResponse struct {
BaseFee github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=base_fee,json=baseFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"base_fee"` BaseFee *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=base_fee,json=baseFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"base_fee,omitempty"`
} }
func (m *QueryBaseFeeResponse) Reset() { *m = QueryBaseFeeResponse{} } func (m *QueryBaseFeeResponse) Reset() { *m = QueryBaseFeeResponse{} }
@ -287,34 +287,34 @@ func init() {
var fileDescriptor_71a07c1ffd85fde2 = []byte{ var fileDescriptor_71a07c1ffd85fde2 = []byte{
// 450 bytes of a gzipped FileDescriptorProto // 450 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xbf, 0x8f, 0xd3, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xcf, 0x6f, 0xd3, 0x30,
0x14, 0x8e, 0x29, 0xf4, 0x0e, 0xb3, 0x20, 0xd3, 0x3b, 0x9d, 0xc2, 0x91, 0x3b, 0x05, 0xe9, 0xc4, 0x14, 0x8e, 0x29, 0x74, 0xc3, 0x5c, 0x90, 0xe9, 0xa6, 0x29, 0x8c, 0x6c, 0x0a, 0xd2, 0x04, 0x83,
0xaf, 0xb3, 0x75, 0xc7, 0xca, 0x94, 0x01, 0xb8, 0x0d, 0xc2, 0xc6, 0x52, 0x39, 0xe5, 0x35, 0x8d, 0xd9, 0xda, 0xb8, 0x72, 0x8a, 0xc4, 0xaf, 0x1b, 0x84, 0x1b, 0x12, 0x9a, 0x9c, 0xf2, 0x9a, 0x46,
0xda, 0xc4, 0x69, 0xec, 0x56, 0x74, 0x85, 0x85, 0x81, 0x01, 0x89, 0x3f, 0x82, 0x7f, 0xa5, 0x63, 0x6d, 0xe2, 0x34, 0x76, 0x2b, 0x7a, 0x85, 0x0b, 0x07, 0x0e, 0x48, 0xfc, 0x11, 0xfc, 0x2b, 0x3d,
0x25, 0x16, 0xc4, 0x50, 0xa1, 0x96, 0x3f, 0x04, 0xc5, 0x71, 0xda, 0x86, 0xb6, 0xd0, 0x29, 0xd6, 0x56, 0xe2, 0x82, 0x38, 0x54, 0xa8, 0xe5, 0x0f, 0x41, 0x71, 0x9c, 0xb6, 0xa1, 0x2d, 0xf4, 0x14,
0xcb, 0xe7, 0xef, 0xfb, 0xde, 0xf7, 0xfc, 0xb0, 0x0b, 0xaa, 0x03, 0x59, 0x1c, 0x25, 0x8a, 0xb5, 0xeb, 0xe5, 0xf3, 0xf7, 0x7d, 0xef, 0x7b, 0x7e, 0xd8, 0x05, 0xd5, 0x86, 0x2c, 0x8e, 0x12, 0xc5,
0x01, 0x62, 0x9e, 0x75, 0x41, 0xb1, 0xe1, 0x05, 0xeb, 0x0f, 0x20, 0x1b, 0xd1, 0x34, 0x13, 0x4a, 0x5a, 0x00, 0x31, 0xcf, 0x3a, 0xa0, 0xd8, 0xe0, 0x9c, 0xf5, 0xfa, 0x90, 0x0d, 0x69, 0x9a, 0x09,
0x90, 0xc3, 0x05, 0x86, 0x2e, 0x30, 0x74, 0x78, 0x61, 0x37, 0x42, 0x11, 0x0a, 0x0d, 0x61, 0xf9, 0x25, 0xc8, 0xfe, 0x1c, 0x43, 0xe7, 0x18, 0x3a, 0x38, 0xb7, 0x1b, 0xa1, 0x08, 0x85, 0x86, 0xb0,
0xa9, 0x40, 0xdb, 0xc7, 0xa1, 0x10, 0x61, 0x0f, 0x18, 0x4f, 0x23, 0xc6, 0x93, 0x44, 0x28, 0xae, 0xfc, 0x54, 0xa0, 0xed, 0xc3, 0x50, 0x88, 0xb0, 0x0b, 0x8c, 0xa7, 0x11, 0xe3, 0x49, 0x22, 0x14,
0x22, 0x91, 0x48, 0xf3, 0xf7, 0x6c, 0x8b, 0xde, 0x92, 0x58, 0xe3, 0xdc, 0x06, 0x26, 0xaf, 0x73, 0x57, 0x91, 0x48, 0xa4, 0xf9, 0x7b, 0xb2, 0x41, 0x6f, 0x41, 0xac, 0x71, 0x6e, 0x03, 0x93, 0x57,
0x0b, 0xaf, 0x78, 0xc6, 0x63, 0xe9, 0x43, 0x7f, 0x00, 0x52, 0xb9, 0x6f, 0xf0, 0x9d, 0x4a, 0x55, 0xb9, 0x85, 0x97, 0x3c, 0xe3, 0xb1, 0xf4, 0xa1, 0xd7, 0x07, 0xa9, 0xdc, 0xd7, 0xf8, 0x56, 0xa5,
0xa6, 0x22, 0x91, 0x40, 0x9e, 0xe1, 0x7a, 0xaa, 0x2b, 0x47, 0xe8, 0x14, 0x3d, 0xb8, 0x75, 0xe9, 0x2a, 0x53, 0x91, 0x48, 0x20, 0x8f, 0x71, 0x3d, 0xd5, 0x95, 0x03, 0x74, 0x8c, 0xee, 0xdd, 0xb8,
0xd0, 0xcd, 0x8e, 0x69, 0x71, 0xcf, 0xbb, 0x3e, 0x9e, 0x9e, 0x58, 0xbe, 0xb9, 0xe3, 0x1e, 0x18, 0x70, 0xe8, 0x7a, 0xc7, 0xb4, 0xb8, 0xe7, 0x5d, 0x1d, 0x4d, 0x8e, 0x2c, 0xdf, 0xdc, 0x71, 0xf7,
0x52, 0x8f, 0x4b, 0x78, 0x0e, 0x50, 0x6a, 0x71, 0xdc, 0xa8, 0x96, 0x8d, 0xd8, 0x15, 0xde, 0x0f, 0x0c, 0xa9, 0xc7, 0x25, 0x3c, 0x05, 0x28, 0xb5, 0xde, 0xe2, 0x46, 0xb5, 0x6c, 0xc4, 0x9e, 0xe0,
0xb8, 0x84, 0x66, 0x1b, 0x40, 0xcb, 0xdd, 0xf4, 0x68, 0x4e, 0xf7, 0x73, 0x7a, 0x72, 0x16, 0x46, 0xdd, 0x80, 0x4b, 0xb8, 0x6c, 0x01, 0x68, 0xb9, 0xeb, 0xde, 0xe9, 0xcf, 0xc9, 0xd1, 0x49, 0x18,
0xaa, 0x33, 0x08, 0x68, 0x4b, 0xc4, 0xac, 0x25, 0x64, 0x2c, 0xa4, 0xf9, 0x9c, 0xcb, 0x77, 0x5d, 0xa9, 0x76, 0x3f, 0xa0, 0x4d, 0x11, 0xb3, 0xa6, 0x90, 0xb1, 0x90, 0xe6, 0x73, 0x26, 0xdf, 0x75,
0xa6, 0x46, 0x29, 0x48, 0x7a, 0x95, 0x28, 0x7f, 0x2f, 0x28, 0x28, 0xdd, 0xc3, 0x52, 0xa2, 0x27, 0x98, 0x1a, 0xa6, 0x20, 0xe9, 0x8b, 0x44, 0xf9, 0x3b, 0x41, 0x41, 0xe7, 0xee, 0x97, 0xf4, 0x5d,
0x5a, 0xdd, 0x17, 0x7c, 0xd1, 0xe6, 0x43, 0x7c, 0xf0, 0x57, 0xdd, 0x68, 0xdf, 0xc6, 0xb5, 0x90, 0xd1, 0xec, 0x3c, 0xe3, 0xf3, 0x16, 0xef, 0xe3, 0xbd, 0xbf, 0xea, 0x46, 0xf7, 0x26, 0xae, 0x85,
0x17, 0x5d, 0xd6, 0xfc, 0xfc, 0x78, 0xf9, 0xad, 0x86, 0x6f, 0x68, 0x2c, 0xf9, 0x88, 0x70, 0xbd, 0xbc, 0xe8, 0xb0, 0xe6, 0xe7, 0xc7, 0x8b, 0x6f, 0x35, 0x7c, 0x4d, 0x63, 0xc9, 0x47, 0x84, 0xeb,
0xe8, 0x8f, 0x3c, 0xda, 0xd6, 0xff, 0x7a, 0xa4, 0xf6, 0xe3, 0x9d, 0xb0, 0x85, 0xbe, 0x7b, 0xfa, 0x45, 0x6f, 0xe4, 0x74, 0x53, 0xef, 0xab, 0x71, 0xda, 0x0f, 0xb6, 0xc2, 0x16, 0xfa, 0xee, 0xf1,
0xe1, 0xfb, 0xef, 0xaf, 0xd7, 0x6c, 0x72, 0xb4, 0x32, 0x3c, 0x18, 0xc6, 0xf9, 0x00, 0x8b, 0x30, 0x87, 0xef, 0xbf, 0xbf, 0x5e, 0xb1, 0xc9, 0xc1, 0xd2, 0xe0, 0x60, 0x10, 0xe7, 0xc3, 0x2b, 0x82,
0xc9, 0x27, 0x84, 0xf7, 0x4c, 0x62, 0xe4, 0xdf, 0xd4, 0xd5, 0xb8, 0xed, 0x27, 0xbb, 0x81, 0x8d, 0x24, 0x9f, 0x10, 0xde, 0x31, 0x69, 0x91, 0x7f, 0x53, 0x57, 0xa3, 0xb6, 0x1f, 0x6e, 0x07, 0x36,
0x11, 0x57, 0x1b, 0x39, 0x26, 0xf6, 0xba, 0x91, 0x72, 0x38, 0xe4, 0x33, 0xc2, 0xfb, 0x65, 0x82, 0x46, 0x5c, 0x6d, 0xe4, 0x90, 0xd8, 0xab, 0x46, 0xca, 0xc1, 0x90, 0xcf, 0x08, 0xef, 0x96, 0x09,
0xe4, 0x3f, 0xf4, 0xd5, 0x01, 0xd8, 0xe7, 0x3b, 0xa2, 0x8d, 0x9b, 0xfb, 0xda, 0xcd, 0x3d, 0x72, 0x92, 0xff, 0xd0, 0x57, 0x07, 0x60, 0x9f, 0x6d, 0x89, 0x36, 0x6e, 0xee, 0x6a, 0x37, 0x77, 0xc8,
0x77, 0x83, 0x9b, 0x1c, 0xdb, 0x0c, 0xb9, 0xf4, 0x5e, 0x8e, 0x67, 0x0e, 0x9a, 0xcc, 0x1c, 0xf4, 0xed, 0x35, 0x6e, 0x72, 0xec, 0x65, 0xc8, 0xa5, 0xf7, 0x7c, 0x34, 0x75, 0xd0, 0x78, 0xea, 0xa0,
0x6b, 0xe6, 0xa0, 0x2f, 0x73, 0xc7, 0x9a, 0xcc, 0x1d, 0xeb, 0xc7, 0xdc, 0xb1, 0xde, 0xd2, 0x95, 0x5f, 0x53, 0x07, 0x7d, 0x99, 0x39, 0xd6, 0x78, 0xe6, 0x58, 0x3f, 0x66, 0x8e, 0xf5, 0x86, 0x2e,
0x77, 0xa3, 0x3a, 0x3c, 0x93, 0x91, 0x64, 0xcb, 0x35, 0x79, 0xbf, 0x42, 0xaa, 0xdf, 0x50, 0x50, 0xbd, 0x1b, 0xd5, 0xe6, 0x99, 0x8c, 0x24, 0x5b, 0xac, 0xc8, 0xfb, 0x25, 0x52, 0xfd, 0x86, 0x82,
0xd7, 0x2b, 0xf2, 0xf4, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xeb, 0xc7, 0xba, 0xe5, 0xbc, 0x03, 0xba, 0x5e, 0x8f, 0x47, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x85, 0x23, 0x79, 0x71, 0xb8, 0x03,
0x00, 0x00, 0x00, 0x00,
} }
@ -575,16 +575,18 @@ func (m *QueryBaseFeeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
{ if m.BaseFee != nil {
size := m.BaseFee.Size() {
i -= size size := m.BaseFee.Size()
if _, err := m.BaseFee.MarshalTo(dAtA[i:]); err != nil { i -= size
return 0, err if _, err := m.BaseFee.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintQuery(dAtA, i, uint64(size))
} }
i = encodeVarintQuery(dAtA, i, uint64(size)) i--
dAtA[i] = 0xa
} }
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
@ -685,8 +687,10 @@ func (m *QueryBaseFeeResponse) Size() (n int) {
} }
var l int var l int
_ = l _ = l
l = m.BaseFee.Size() if m.BaseFee != nil {
n += 1 + l + sovQuery(uint64(l)) l = m.BaseFee.Size()
n += 1 + l + sovQuery(uint64(l))
}
return n return n
} }
@ -959,6 +963,8 @@ func (m *QueryBaseFeeResponse) Unmarshal(dAtA []byte) error {
if postIndex > l { if postIndex > l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
var v github_com_cosmos_cosmos_sdk_types.Int
m.BaseFee = &v
if err := m.BaseFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { if err := m.BaseFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err return err
} }