laconicd-deprecated/rpc/ethereum/namespaces/eth/api.go

1001 lines
30 KiB
Go
Raw Normal View History

package eth
2021-04-18 16:39:15 +00:00
import (
"context"
"encoding/json"
"fmt"
"math"
2021-04-18 16:39:15 +00:00
"math/big"
"strings"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
2021-04-18 16:39:15 +00:00
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/log"
2021-04-18 16:39:15 +00:00
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
2021-04-18 16:39:15 +00:00
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
2021-04-18 16:39:15 +00:00
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/ethereum/go-ethereum/accounts/keystore"
2021-04-18 16:39:15 +00:00
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
2021-04-18 16:39:15 +00:00
"github.com/tharsis/ethermint/crypto/hd"
"github.com/tharsis/ethermint/rpc/ethereum/backend"
rpctypes "github.com/tharsis/ethermint/rpc/ethereum/types"
ethermint "github.com/tharsis/ethermint/types"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
2021-04-18 16:39:15 +00:00
)
// PublicAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
type PublicAPI struct {
2021-04-18 16:39:15 +00:00
ctx context.Context
clientCtx client.Context
queryClient *rpctypes.QueryClient
2021-04-18 16:39:15 +00:00
chainIDEpoch *big.Int
logger log.Logger
backend backend.Backend
nonceLock *rpctypes.AddrLocker
signer ethtypes.Signer
2021-04-18 16:39:15 +00:00
}
// NewPublicAPI creates an instance of the public ETH Web3 API.
func NewPublicAPI(
logger log.Logger,
2021-04-18 16:39:15 +00:00
clientCtx client.Context,
backend backend.Backend,
nonceLock *rpctypes.AddrLocker,
) *PublicAPI {
eip155ChainID, err := ethermint.ParseChainID(clientCtx.ChainID)
2021-04-18 16:39:15 +00:00
if err != nil {
panic(err)
}
algos, _ := clientCtx.Keyring.SupportedAlgorithms()
if !algos.Contains(hd.EthSecp256k1) {
kr, err := keyring.New(
sdk.KeyringServiceName(),
viper.GetString(flags.FlagKeyringBackend),
clientCtx.KeyringDir,
clientCtx.Input,
hd.EthSecp256k1Option(),
)
if err != nil {
panic(err)
}
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.
cfg := backend.ChainConfig()
if cfg == nil {
cfg = evmtypes.DefaultChainConfig().EthereumConfig(eip155ChainID)
}
signer := ethtypes.LatestSigner(cfg)
api := &PublicAPI{
2021-04-18 16:39:15 +00:00
ctx: context.Background(),
clientCtx: clientCtx,
queryClient: rpctypes.NewQueryClient(clientCtx),
chainIDEpoch: eip155ChainID,
logger: logger.With("client", "json-rpc"),
2021-04-18 16:39:15 +00:00
backend: backend,
nonceLock: nonceLock,
signer: signer,
2021-04-18 16:39:15 +00:00
}
return api
}
// ClientCtx returns client context
func (e *PublicAPI) ClientCtx() client.Context {
return e.clientCtx
}
rpc: `miner_` namespace (#377) * miner namespace * SetGasPrice call * Added note plus fixed error logging * Refactor to use the keyring in the miner namespace * Changed keyring function return * Added more detailed logs to unsupported functions * Reverted changes on ethapi and just using a refrence to it on miner * Creating a transaction * fix condition * Error string not capitalized * suggested changes to setEtherbase * change log level * minor changes * minor changes * Sending tx to test the endpoint * get tx nonce * Using aphoton const and changing the logger to debug * Using default RPC gas limit constant * Apply suggestions from code review Renames and log changes Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * pair programming session * get gas * Set gas prices note added * Setting fess and max gas * delete unnecessary log * Apply suggestions from code review return false in case of error Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Suggested changes applied * Updated changelog and json_rpc docs * Update CHANGELOG.md Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Using the same coin denom as the gas price for the fee Co-authored-by: ramacarlucho <ramirocarlucho@gmail.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Federico Kunze <federico.kunze94@gmail.com>
2021-08-04 09:18:22 +00:00
func (e *PublicAPI) QueryClient() *rpctypes.QueryClient {
return e.queryClient
}
func (e *PublicAPI) Ctx() context.Context {
return e.ctx
}
2021-04-18 16:39:15 +00:00
// ProtocolVersion returns the supported Ethereum protocol version.
func (e *PublicAPI) ProtocolVersion() hexutil.Uint {
e.logger.Debug("eth_protocolVersion")
return hexutil.Uint(ethermint.ProtocolVersion)
2021-04-18 16:39:15 +00:00
}
// ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config.
func (e *PublicAPI) ChainId() (*hexutil.Big, error) { // nolint
e.logger.Debug("eth_chainId")
// 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")
2021-04-18 16:39:15 +00:00
}
// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not
// 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) {
e.logger.Debug("eth_syncing")
2021-04-18 16:39:15 +00:00
status, err := e.clientCtx.Client.Status(e.ctx)
if err != nil {
return false, err
}
if !status.SyncInfo.CatchingUp {
return false, nil
}
return map[string]interface{}{
"startingBlock": hexutil.Uint64(status.SyncInfo.EarliestBlockHeight),
"currentBlock": hexutil.Uint64(status.SyncInfo.LatestBlockHeight),
2021-04-18 16:39:15 +00:00
// "highestBlock": nil, // NA
// "pulledStates": nil, // NA
// "knownStates": nil, // NA
}, nil
}
// Coinbase is the address that staking rewards will be send to (alias for Etherbase).
func (e *PublicAPI) Coinbase() (string, error) {
e.logger.Debug("eth_coinbase")
2021-04-18 16:39:15 +00:00
rpc: `miner_` namespace (#377) * miner namespace * SetGasPrice call * Added note plus fixed error logging * Refactor to use the keyring in the miner namespace * Changed keyring function return * Added more detailed logs to unsupported functions * Reverted changes on ethapi and just using a refrence to it on miner * Creating a transaction * fix condition * Error string not capitalized * suggested changes to setEtherbase * change log level * minor changes * minor changes * Sending tx to test the endpoint * get tx nonce * Using aphoton const and changing the logger to debug * Using default RPC gas limit constant * Apply suggestions from code review Renames and log changes Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * pair programming session * get gas * Set gas prices note added * Setting fess and max gas * delete unnecessary log * Apply suggestions from code review return false in case of error Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Suggested changes applied * Updated changelog and json_rpc docs * Update CHANGELOG.md Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Using the same coin denom as the gas price for the fee Co-authored-by: ramacarlucho <ramirocarlucho@gmail.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Federico Kunze <federico.kunze94@gmail.com>
2021-08-04 09:18:22 +00:00
coinbase, err := e.backend.GetCoinbase()
2021-04-18 16:39:15 +00:00
if err != nil {
return "", err
2021-04-18 16:39:15 +00:00
}
rpc: `miner_` namespace (#377) * miner namespace * SetGasPrice call * Added note plus fixed error logging * Refactor to use the keyring in the miner namespace * Changed keyring function return * Added more detailed logs to unsupported functions * Reverted changes on ethapi and just using a refrence to it on miner * Creating a transaction * fix condition * Error string not capitalized * suggested changes to setEtherbase * change log level * minor changes * minor changes * Sending tx to test the endpoint * get tx nonce * Using aphoton const and changing the logger to debug * Using default RPC gas limit constant * Apply suggestions from code review Renames and log changes Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * pair programming session * get gas * Set gas prices note added * Setting fess and max gas * delete unnecessary log * Apply suggestions from code review return false in case of error Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Suggested changes applied * Updated changelog and json_rpc docs * Update CHANGELOG.md Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/rpc/namespaces/miner/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Using the same coin denom as the gas price for the fee Co-authored-by: ramacarlucho <ramirocarlucho@gmail.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Federico Kunze <federico.kunze94@gmail.com>
2021-08-04 09:18:22 +00:00
ethAddr := common.BytesToAddress(coinbase.Bytes())
return ethAddr.Hex(), nil
2021-04-18 16:39:15 +00:00
}
// Mining returns whether or not this node is currently mining. Always false.
func (e *PublicAPI) Mining() bool {
e.logger.Debug("eth_mining")
2021-04-18 16:39:15 +00:00
return false
}
// Hashrate returns the current node's hashrate. Always 0.
func (e *PublicAPI) Hashrate() hexutil.Uint64 {
e.logger.Debug("eth_hashrate")
2021-04-18 16:39:15 +00:00
return 0
}
// GasPrice returns the current gas price based on Ethermint's gas price oracle.
func (e *PublicAPI) GasPrice() (*hexutil.Big, error) {
e.logger.Debug("eth_gasPrice")
tipcap, err := e.backend.SuggestGasTipCap()
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")
rpc: `eth_feeHistory` (#734) * Problem: missing json rpc of eth_feeHistory #685 add oracle backend space ready structure ok refactoring return feehistory data flow ok basefee set gas used ratio computing reward add testing add gas used prepare data fill reward increase coin fixing api add mac add launch gas used ratio ok print element reward workes reward working fix panic value correct remove debugging log tidy up tidy up remove oracle tidy up fix handler crash add unit test tidy up add limit check reformat fix lint fix lint fix lint fix lint Update rpc/ethereum/backend/feebackend.go thanks Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Update rpc/ethereum/backend/feebackend.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Update rpc/ethereum/backend/feebackend.go thanks Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Update rpc/ethereum/backend/feebackend.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> fix compile error split lines remove temporary string conversion return error if gaslimit is 0 move OneFeeHistory to types add comment only err check Update rpc/ethereum/backend/feebackend.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Update rpc/ethereum/backend/feebackend.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> tidy up add feehistory-cap * Apply suggestions from code review * changelog Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Federico Kunze Küllmer <federico.kunze94@gmail.com>
2021-11-17 11:58:52 +00:00
return e.backend.FeeHistory(blockCount, lastBlock, rewardPercentiles)
2021-04-18 16:39:15 +00:00
}
// Accounts returns the list of accounts available to this node.
func (e *PublicAPI) Accounts() ([]common.Address, error) {
e.logger.Debug("eth_accounts")
2021-04-18 16:39:15 +00:00
2021-04-19 10:49:55 +00:00
addresses := make([]common.Address, 0) // return [] instead of nil if empty
infos, err := e.clientCtx.Keyring.List()
if err != nil {
return addresses, err
}
for _, info := range infos {
addressBytes := info.GetPubKey().Address().Bytes()
addresses = append(addresses, common.BytesToAddress(addressBytes))
}
return addresses, nil
2021-04-18 16:39:15 +00:00
}
// BlockNumber returns the current block number.
func (e *PublicAPI) BlockNumber() (hexutil.Uint64, error) {
e.logger.Debug("eth_blockNumber")
2021-04-18 16:39:15 +00:00
return e.backend.BlockNumber()
}
// GetBalance returns the provided account's balance up to the provided block number.
func (e *PublicAPI) GetBalance(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Big, error) {
e.logger.Debug("eth_getBalance", "address", address.String(), "block number or hash", blockNrOrHash)
blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}
2021-04-18 16:39:15 +00:00
req := &evmtypes.QueryBalanceRequest{
Address: address.String(),
}
res, err := e.queryClient.Balance(rpctypes.ContextWithHeight(blockNum.Int64()), req)
2021-04-18 16:39:15 +00:00
if err != nil {
return nil, err
}
val, ok := sdk.NewIntFromString(res.Balance)
if !ok {
return nil, errors.New("invalid balance")
}
return (*hexutil.Big)(val.BigInt()), nil
2021-04-18 16:39:15 +00:00
}
// GetStorageAt returns the contract storage at the given address, block number, and key.
func (e *PublicAPI) GetStorageAt(address common.Address, key string, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) {
e.logger.Debug("eth_getStorageAt", "address", address.Hex(), "key", key, "block number or hash", blockNrOrHash)
blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}
2021-04-18 16:39:15 +00:00
req := &evmtypes.QueryStorageRequest{
Address: address.String(),
Key: key,
}
res, err := e.queryClient.Storage(rpctypes.ContextWithHeight(blockNum.Int64()), req)
2021-04-18 16:39:15 +00:00
if err != nil {
return nil, err
}
value := common.HexToHash(res.Value)
return value.Bytes(), nil
}
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
func (e *PublicAPI) GetTransactionCount(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Uint64, error) {
e.logger.Debug("eth_getTransactionCount", "address", address.Hex(), "block number or hash", blockNrOrHash)
blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}
return e.backend.GetTransactionCount(address, blockNum)
2021-04-18 16:39:15 +00:00
}
// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash.
func (e *PublicAPI) GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint {
e.logger.Debug("eth_getBlockTransactionCountByHash", "hash", hash.Hex())
2021-04-18 16:39:15 +00:00
resBlock, err := e.clientCtx.Client.BlockByHash(e.ctx, hash.Bytes())
if err != nil {
e.logger.Debug("block not found", "hash", hash.Hex(), "error", err.Error())
return nil
}
if resBlock.Block == nil {
e.logger.Debug("block not found", "hash", hash.Hex())
2021-04-18 16:39:15 +00:00
return nil
}
ethMsgs := e.backend.GetEthereumMsgsFromTendermintBlock(resBlock)
n := hexutil.Uint(len(ethMsgs))
2021-04-18 16:39:15 +00:00
return &n
}
// GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number.
func (e *PublicAPI) GetBlockTransactionCountByNumber(blockNum rpctypes.BlockNumber) *hexutil.Uint {
e.logger.Debug("eth_getBlockTransactionCountByNumber", "height", blockNum.Int64())
2021-04-18 16:39:15 +00:00
resBlock, err := e.clientCtx.Client.Block(e.ctx, blockNum.TmHeight())
if err != nil {
e.logger.Debug("block not found", "height", blockNum.Int64(), "error", err.Error())
return nil
}
if resBlock.Block == nil {
e.logger.Debug("block not found", "height", blockNum.Int64())
2021-04-18 16:39:15 +00:00
return nil
}
ethMsgs := e.backend.GetEthereumMsgsFromTendermintBlock(resBlock)
n := hexutil.Uint(len(ethMsgs))
2021-04-18 16:39:15 +00:00
return &n
}
// GetUncleCountByBlockHash returns the number of uncles in the block identified by hash. Always zero.
func (e *PublicAPI) GetUncleCountByBlockHash(hash common.Hash) hexutil.Uint {
2021-04-18 16:39:15 +00:00
return 0
}
// GetUncleCountByBlockNumber returns the number of uncles in the block identified by number. Always zero.
func (e *PublicAPI) GetUncleCountByBlockNumber(blockNum rpctypes.BlockNumber) hexutil.Uint {
2021-04-18 16:39:15 +00:00
return 0
}
// GetCode returns the contract code at the given address and block number.
func (e *PublicAPI) GetCode(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) {
e.logger.Debug("eth_getCode", "address", address.Hex(), "block number or hash", blockNrOrHash)
blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}
2021-04-18 16:39:15 +00:00
req := &evmtypes.QueryCodeRequest{
Address: address.String(),
}
res, err := e.queryClient.Code(rpctypes.ContextWithHeight(blockNum.Int64()), req)
2021-04-18 16:39:15 +00:00
if err != nil {
return nil, err
}
return res.Code, nil
}
// GetTransactionLogs returns the logs given a transaction hash.
func (e *PublicAPI) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error) {
e.logger.Debug("eth_getTransactionLogs", "hash", txHash)
2021-04-18 16:39:15 +00:00
return e.backend.GetTransactionLogs(txHash)
}
// Sign signs the provided data using the private key of address via Geth's signature standard.
func (e *PublicAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
e.logger.Debug("eth_sign", "address", address.Hex(), "data", common.Bytes2Hex(data))
from := sdk.AccAddress(address.Bytes())
_, err := e.clientCtx.Keyring.KeyByAddress(from)
if err != nil {
e.logger.Error("failed to find key in keyring", "address", address.String())
return nil, fmt.Errorf("%s; %s", keystore.ErrNoMatch, err.Error())
}
// Sign the requested hash with the wallet
signature, _, err := e.clientCtx.Keyring.SignByAddress(from, data)
if err != nil {
e.logger.Error("keyring.SignByAddress failed", "address", address.Hex())
return nil, err
}
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
return signature, nil
2021-04-18 16:39:15 +00:00
}
// SendTransaction sends an Ethereum transaction.
func (e *PublicAPI) SendTransaction(args evmtypes.TransactionArgs) (common.Hash, error) {
e.logger.Debug("eth_sendTransaction", "args", args.String())
return e.backend.SendTransaction(args)
2021-04-18 16:39:15 +00:00
}
// 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
}
2021-04-18 16:39:15 +00:00
// SendRawTransaction send a raw Ethereum transaction.
func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) {
e.logger.Debug("eth_sendRawTransaction", "length", len(data))
2021-04-18 16:39:15 +00:00
// RLP decode raw transaction bytes
tx := &ethtypes.Transaction{}
if err := tx.UnmarshalBinary(data); err != nil {
e.logger.Error("transaction decoding failed", "error", err.Error())
2021-04-18 16:39:15 +00:00
return common.Hash{}, err
}
ethereumTx := &evmtypes.MsgEthereumTx{}
if err := ethereumTx.FromEthereumTx(tx); err != nil {
e.logger.Error("transaction converting failed", "error", err.Error())
return common.Hash{}, err
}
if err := ethereumTx.ValidateBasic(); err != nil {
e.logger.Debug("tx failed basic validation", "error", err.Error())
return common.Hash{}, err
}
// Query params to use the EVM denomination
res, err := e.queryClient.QueryClient.Params(e.ctx, &evmtypes.QueryParamsRequest{})
if err != nil {
e.logger.Error("failed to query evm params", "error", err.Error())
return common.Hash{}, err
}
cosmosTx, err := ethereumTx.BuildTx(e.clientCtx.TxConfig.NewTxBuilder(), res.Params.EvmDenom)
if err != nil {
e.logger.Error("failed to build cosmos tx", "error", err.Error())
return common.Hash{}, err
}
2021-04-18 16:39:15 +00:00
// Encode transaction by default Tx encoder
txBytes, err := e.clientCtx.TxConfig.TxEncoder()(cosmosTx)
2021-04-18 16:39:15 +00:00
if err != nil {
e.logger.Error("failed to encode eth tx using default encoder", "error", err.Error())
2021-04-18 16:39:15 +00:00
return common.Hash{}, err
}
txHash := ethereumTx.AsTransaction().Hash()
syncCtx := e.clientCtx.WithBroadcastMode(flags.BroadcastSync)
rsp, err := syncCtx.BroadcastTx(txBytes)
if rsp != nil && rsp.Code != 0 {
err = sdkerrors.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog)
}
if err != nil {
e.logger.Error("failed to broadcast tx", "error", err.Error())
2021-04-18 16:39:15 +00:00
return txHash, err
}
return txHash, nil
}
// checkTxFee is an internal function used to check whether the fee of
// the given transaction is _reasonable_(under the cap).
func checkTxFee(gasPrice *big.Int, gas uint64, cap float64) error {
// Short circuit if there is no cap for transaction fee at all.
if cap == 0 {
return nil
}
totalfee := new(big.Float).SetInt(new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gas)))
// 1 photon in 10^18 aphoton
oneToken := new(big.Float).SetInt(big.NewInt(params.Ether))
// quo = rounded(x/y)
feeEth := new(big.Float).Quo(totalfee, oneToken)
// no need to check error from parsing
feeFloat, _ := feeEth.Float64()
if feeFloat > cap {
return fmt.Errorf("tx fee (%.2f ether) exceeds the configured cap (%.2f ether)", feeFloat, cap)
}
return 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()
// Before replacing the old transaction, ensure the _new_ transaction fee is reasonable.
price := matchTx.GasPrice()
if gasPrice != nil {
price = gasPrice.ToInt()
}
gas := matchTx.Gas()
if gasLimit != nil {
gas = uint64(*gasLimit)
}
if err := checkTxFee(price, gas, e.backend.RPCTxFeeCap()); err != nil {
return common.Hash{}, err
}
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())
}
2021-04-18 16:39:15 +00:00
// Call performs a raw contract call.
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)
blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}
data, err := e.doCall(args, blockNum)
2021-04-18 16:39:15 +00:00
if err != nil {
return []byte{}, err
}
2021-04-18 16:39:15 +00:00
return (hexutil.Bytes)(data.Ret), nil
}
// DoCall performs a simulated call operation through the evmtypes. It returns the
// estimated gas used on the operation or an error if fails.
func (e *PublicAPI) doCall(
args evmtypes.TransactionArgs, blockNr rpctypes.BlockNumber,
) (*evmtypes.MsgEthereumTxResponse, error) {
bz, err := json.Marshal(&args)
if err != nil {
return nil, err
}
req := evmtypes.EthCallRequest{
Args: bz,
GasCap: e.backend.RPCGasCap(),
}
// From ContextWithHeight: if the provided height is 0,
// it will return an empty context and the gRPC query will use
// the latest block height for querying.
build(deps): bump github.com/ethereum/go-ethereum from 1.10.9 to 1.10.11 (#676) * build(deps): bump github.com/ethereum/go-ethereum from 1.10.9 to 1.10.10 Bumps [github.com/ethereum/go-ethereum](https://github.com/ethereum/go-ethereum) from 1.10.9 to 1.10.10. - [Release notes](https://github.com/ethereum/go-ethereum/releases) - [Commits](https://github.com/ethereum/go-ethereum/compare/v1.10.9...v1.10.10) --- updated-dependencies: - dependency-name: github.com/ethereum/go-ethereum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * build(deps): bump github.com/ethereum/go-ethereum from 1.10.9 to 1.10.10 Bumps [github.com/ethereum/go-ethereum](https://github.com/ethereum/go-ethereum) from 1.10.9 to 1.10.10. - [Release notes](https://github.com/ethereum/go-ethereum/releases) - [Commits](https://github.com/ethereum/go-ethereum/compare/v1.10.9...v1.10.10) --- updated-dependencies: - dependency-name: github.com/ethereum/go-ethereum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * build(deps): bump github.com/ethereum/go-ethereum from 1.10.9 to 1.10.10 Bumps [github.com/ethereum/go-ethereum](https://github.com/ethereum/go-ethereum) from 1.10.9 to 1.10.10. - [Release notes](https://github.com/ethereum/go-ethereum/releases) - [Commits](https://github.com/ethereum/go-ethereum/compare/v1.10.9...v1.10.10) --- updated-dependencies: - dependency-name: github.com/ethereum/go-ethereum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * fix * rpc: RLP apis * tx fee cap fix * fix config * fix test Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Federico Kunze <federico.kunze94@gmail.com>
2021-10-25 15:01:04 +00:00
ctx := rpctypes.ContextWithHeight(blockNr.Int64())
timeout := e.backend.RPCEVMTimeout()
// Setup context so it may be canceled the call has completed
// or, in case of unmetered gas, setup a context with a timeout.
var cancel context.CancelFunc
if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, timeout)
} else {
ctx, cancel = context.WithCancel(ctx)
}
// Make sure the context is canceled when the call has completed
// this makes sure resources are cleaned up.
defer cancel()
res, err := e.queryClient.EthCall(ctx, &req)
if err != nil {
return nil, err
}
if res.Failed() {
if res.VmError != vm.ErrExecutionReverted.Error() {
return nil, status.Error(codes.Internal, res.VmError)
}
return nil, evmtypes.NewExecErrorWithReason(res.Ret)
}
return res, nil
2021-04-18 16:39:15 +00:00
}
// EstimateGas returns an estimate of gas usage for the given smart contract call.
func (e *PublicAPI) EstimateGas(args evmtypes.TransactionArgs, blockNrOptional *rpctypes.BlockNumber) (hexutil.Uint64, error) {
e.logger.Debug("eth_estimateGas")
return e.backend.EstimateGas(args, blockNrOptional)
2021-04-18 16:39:15 +00:00
}
// GetBlockByHash returns the block identified by hash.
func (e *PublicAPI) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) {
e.logger.Debug("eth_getBlockByHash", "hash", hash.Hex(), "full", fullTx)
2021-04-18 16:39:15 +00:00
return e.backend.GetBlockByHash(hash, fullTx)
}
// GetBlockByNumber returns the block identified by number.
func (e *PublicAPI) GetBlockByNumber(ethBlockNum rpctypes.BlockNumber, fullTx bool) (map[string]interface{}, error) {
e.logger.Debug("eth_getBlockByNumber", "number", ethBlockNum, "full", fullTx)
2021-04-18 16:39:15 +00:00
return e.backend.GetBlockByNumber(ethBlockNum, fullTx)
}
// GetTransactionByHash returns the transaction identified by hash.
func (e *PublicAPI) GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransaction, error) {
e.logger.Debug("eth_getTransactionByHash", "hash", hash.Hex())
return e.backend.GetTransactionByHash(hash)
2021-04-18 16:39:15 +00:00
}
// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index.
func (e *PublicAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) {
e.logger.Debug("eth_getTransactionByBlockHashAndIndex", "hash", hash.Hex(), "index", idx)
2021-04-18 16:39:15 +00:00
resBlock, err := e.clientCtx.Client.BlockByHash(e.ctx, hash.Bytes())
2021-04-18 16:39:15 +00:00
if err != nil {
e.logger.Debug("block not found", "hash", hash.Hex(), "error", err.Error())
return nil, nil
}
if resBlock.Block == nil {
e.logger.Debug("block not found", "hash", hash.Hex())
return nil, nil
}
i := int(idx)
ethMsgs := e.backend.GetEthereumMsgsFromTendermintBlock(resBlock)
if i >= len(ethMsgs) {
e.logger.Debug("block txs index out of bound", "index", i)
return nil, nil
}
msg := ethMsgs[i]
baseFee, err := e.backend.BaseFee(resBlock.Block.Height)
if err != nil {
return nil, err
}
return rpctypes.NewTransactionFromMsg(
msg,
hash,
uint64(resBlock.Block.Height),
uint64(idx),
baseFee,
)
2021-04-18 16:39:15 +00:00
}
// GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index.
func (e *PublicAPI) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) {
e.logger.Debug("eth_getTransactionByBlockNumberAndIndex", "number", blockNum, "index", idx)
2021-04-18 16:39:15 +00:00
resBlock, err := e.clientCtx.Client.Block(e.ctx, blockNum.TmHeight())
2021-04-18 16:39:15 +00:00
if err != nil {
e.logger.Debug("block not found", "height", blockNum.Int64(), "error", err.Error())
return nil, nil
2021-04-18 16:39:15 +00:00
}
if resBlock.Block == nil {
e.logger.Debug("block not found", "height", blockNum.Int64())
return nil, nil
}
i := int(idx)
ethMsgs := e.backend.GetEthereumMsgsFromTendermintBlock(resBlock)
if i >= len(ethMsgs) {
e.logger.Debug("block txs index out of bound", "index", i)
2021-04-18 16:39:15 +00:00
return nil, nil
}
msg := ethMsgs[i]
return rpctypes.NewTransactionFromMsg(
msg,
common.BytesToHash(resBlock.Block.Hash()),
uint64(resBlock.Block.Height),
2021-04-18 16:39:15 +00:00
uint64(idx),
e.chainIDEpoch,
2021-04-18 16:39:15 +00:00
)
}
// GetTransactionReceipt returns the transaction receipt identified by hash.
func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) {
hexTx := hash.Hex()
e.logger.Debug("eth_getTransactionReceipt", "hash", hexTx)
2021-04-18 16:39:15 +00:00
res, err := e.backend.GetTxByEthHash(hash)
2021-04-18 16:39:15 +00:00
if err != nil {
e.logger.Debug("tx not found", "hash", hexTx, "error", err.Error())
2021-04-18 16:39:15 +00:00
return nil, nil
}
resBlock, err := e.clientCtx.Client.Block(e.ctx, &res.Height)
2021-04-18 16:39:15 +00:00
if err != nil {
e.logger.Debug("block not found", "height", res.Height, "error", err.Error())
return nil, nil
}
tx, err := e.clientCtx.TxConfig.TxDecoder()(res.Tx)
if err != nil {
e.logger.Debug("decoding failed", "error", err.Error())
return nil, fmt.Errorf("failed to decode tx: %w", err)
}
msg, err := evmtypes.UnwrapEthereumMsg(&tx)
if err != nil {
e.logger.Debug("invalid tx", "error", err.Error())
return nil, err
}
txData, err := evmtypes.UnpackTxData(msg.Data)
if err != nil {
e.logger.Error("failed to unpack tx data", "error", err.Error())
return nil, err
}
cumulativeGasUsed := uint64(0)
blockRes, err := e.clientCtx.Client.BlockResults(e.ctx, &res.Height)
if err != nil {
e.logger.Debug("failed to retrieve block results", "height", res.Height, "error", err.Error())
return nil, nil
2021-04-18 16:39:15 +00:00
}
for i := 0; i <= int(res.Index) && i < len(blockRes.TxsResults); i++ {
cumulativeGasUsed += uint64(blockRes.TxsResults[i].GasUsed)
2021-04-18 16:39:15 +00:00
}
// Get the transaction result from the log
2021-04-18 16:39:15 +00:00
var status hexutil.Uint
if strings.Contains(res.TxResult.GetLog(), evmtypes.AttributeKeyEthereumTxFailed) {
status = hexutil.Uint(ethtypes.ReceiptStatusFailed)
} else {
status = hexutil.Uint(ethtypes.ReceiptStatusSuccessful)
2021-04-18 16:39:15 +00:00
}
from, err := msg.GetSender(e.chainIDEpoch)
if err != nil {
return nil, err
}
logs, err := e.backend.GetTransactionLogs(hash)
if err != nil {
e.logger.Debug("logs not found", "hash", hexTx, "error", err.Error())
}
// get eth index based on block's txs
var txIndex uint64
msgs := e.backend.GetEthereumMsgsFromTendermintBlock(resBlock)
for i := range msgs {
if msgs[i].Hash == hexTx {
txIndex = uint64(i)
break
}
2021-04-18 16:39:15 +00:00
}
receipt := map[string]interface{}{
// Consensus fields: These fields are defined by the Yellow Paper
"status": status,
"cumulativeGasUsed": hexutil.Uint64(cumulativeGasUsed),
"logsBloom": ethtypes.BytesToBloom(ethtypes.LogsBloom(logs)),
"logs": logs,
2021-04-18 16:39:15 +00:00
// Implementation fields: These fields are added by geth when processing a transaction.
// They are stored in the chain database.
"transactionHash": hash,
"contractAddress": nil,
"gasUsed": hexutil.Uint64(res.TxResult.GasUsed),
"type": hexutil.Uint(txData.TxType()),
2021-04-18 16:39:15 +00:00
// Inclusion information: These fields provide information about the inclusion of the
// transaction corresponding to this receipt.
"blockHash": common.BytesToHash(resBlock.Block.Header.Hash()).Hex(),
"blockNumber": hexutil.Uint64(res.Height),
"transactionIndex": hexutil.Uint64(txIndex),
2021-04-18 16:39:15 +00:00
// sender and receiver (contract or EOA) addreses
"from": from,
"to": txData.GetTo(),
}
if logs == nil {
receipt["logs"] = [][]*ethtypes.Log{}
}
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
if txData.GetTo() == nil {
receipt["contractAddress"] = crypto.CreateAddress(from, txData.GetNonce())
2021-04-18 16:39:15 +00:00
}
return receipt, nil
}
// GetPendingTransactions returns the transactions that are in the transaction pool
2021-04-18 16:39:15 +00:00
// and have a from address that is one of the accounts this node manages.
func (e *PublicAPI) GetPendingTransactions() ([]*rpctypes.RPCTransaction, error) {
e.logger.Debug("eth_getPendingTransactions")
txs, err := e.backend.PendingTransactions()
if err != nil {
return nil, err
}
result := make([]*rpctypes.RPCTransaction, 0, len(txs))
for _, tx := range txs {
msg, err := evmtypes.UnwrapEthereumMsg(tx)
if err != nil {
// not valid ethereum tx
continue
}
rpctx, err := rpctypes.NewTransactionFromMsg(
msg,
common.Hash{},
uint64(0),
uint64(0),
e.chainIDEpoch,
)
if err != nil {
return nil, err
}
result = append(result, rpctx)
}
return result, nil
2021-04-18 16:39:15 +00:00
}
// GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. Always returns nil.
func (e *PublicAPI) GetUncleByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) map[string]interface{} {
2021-04-18 16:39:15 +00:00
return nil
}
// GetUncleByBlockNumberAndIndex returns the uncle identified by number and index. Always returns nil.
func (e *PublicAPI) GetUncleByBlockNumberAndIndex(number, idx hexutil.Uint) map[string]interface{} {
2021-04-18 16:39:15 +00:00
return nil
}
// GetProof returns an account object with proof and any storage proofs
func (e *PublicAPI) GetProof(address common.Address, storageKeys []string, blockNrOrHash rpctypes.BlockNumberOrHash) (*rpctypes.AccountResult, error) {
e.logger.Debug("eth_getProof", "address", address.Hex(), "keys", storageKeys, "block number or hash", blockNrOrHash)
blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}
2021-04-18 16:39:15 +00:00
height := blockNum.Int64()
ctx := rpctypes.ContextWithHeight(height)
// if the height is equal to zero, meaning the query condition of the block is either "pending" or "latest"
if height == 0 {
bn, err := e.backend.BlockNumber()
if err != nil {
return nil, err
}
if bn > math.MaxInt64 {
return nil, fmt.Errorf("not able to query block number greater than MaxInt64")
}
height = int64(bn)
}
2021-04-18 16:39:15 +00:00
clientCtx := e.clientCtx.WithHeight(height)
// query storage proofs
storageProofs := make([]rpctypes.StorageResult, len(storageKeys))
2021-04-18 16:39:15 +00:00
for i, key := range storageKeys {
hexKey := common.HexToHash(key)
valueBz, proof, err := e.queryClient.GetProof(clientCtx, evmtypes.StoreKey, evmtypes.StateKey(address, hexKey.Bytes()))
if err != nil {
return nil, err
}
// check for proof
var proofStr string
if proof != nil {
proofStr = proof.String()
}
storageProofs[i] = rpctypes.StorageResult{
2021-04-18 16:39:15 +00:00
Key: key,
Value: (*hexutil.Big)(new(big.Int).SetBytes(valueBz)),
Proof: []string{proofStr},
}
}
// query EVM account
req := &evmtypes.QueryAccountRequest{
Address: address.String(),
}
res, err := e.queryClient.Account(ctx, req)
if err != nil {
return nil, err
}
// query account proofs
accountKey := authtypes.AddressStoreKey(sdk.AccAddress(address.Bytes()))
_, proof, err := e.queryClient.GetProof(clientCtx, authtypes.StoreKey, accountKey)
if err != nil {
return nil, err
}
// check for proof
var accProofStr string
if proof != nil {
accProofStr = proof.String()
}
balance, ok := sdk.NewIntFromString(res.Balance)
if !ok {
return nil, errors.New("invalid balance")
}
return &rpctypes.AccountResult{
2021-04-18 16:39:15 +00:00
Address: address,
AccountProof: []string{accProofStr},
Balance: (*hexutil.Big)(balance.BigInt()),
CodeHash: common.HexToHash(res.CodeHash),
2021-04-18 16:39:15 +00:00
Nonce: hexutil.Uint64(res.Nonce),
StorageHash: common.Hash{}, // NOTE: Ethermint doesn't have a storage hash. TODO: implement?
StorageProof: storageProofs,
}, nil
}
// getBlockNumber returns the BlockNumber from BlockNumberOrHash
func (e *PublicAPI) getBlockNumber(blockNrOrHash rpctypes.BlockNumberOrHash) (rpctypes.BlockNumber, error) {
switch {
case blockNrOrHash.BlockHash == nil && blockNrOrHash.BlockNumber == nil:
2021-08-26 15:45:45 +00:00
return rpctypes.EthEarliestBlockNumber, fmt.Errorf("types BlockHash and BlockNumber cannot be both nil")
case blockNrOrHash.BlockHash != nil:
blockHeader, err := e.backend.HeaderByHash(*blockNrOrHash.BlockHash)
if err != nil {
return rpctypes.EthEarliestBlockNumber, err
}
return rpctypes.NewBlockNumber(blockHeader.Number), nil
case blockNrOrHash.BlockNumber != nil:
return *blockNrOrHash.BlockNumber, nil
default:
return rpctypes.EthEarliestBlockNumber, nil
}
}