rpc: refactor backend package (#418)
* backend refractor * Revert init file changes * fix linter issues * Update ethereum/rpc/namespaces/personal/api.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
parent
9227e78c79
commit
640684c648
@ -37,10 +37,8 @@ const (
|
|||||||
func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API {
|
func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API {
|
||||||
nonceLock := new(types.AddrLocker)
|
nonceLock := new(types.AddrLocker)
|
||||||
evmBackend := backend.NewEVMBackend(ctx.Logger, clientCtx)
|
evmBackend := backend.NewEVMBackend(ctx.Logger, clientCtx)
|
||||||
ethAPI := eth.NewPublicAPI(ctx.Logger, clientCtx, evmBackend, nonceLock)
|
|
||||||
|
|
||||||
var apis []rpc.API
|
var apis []rpc.API
|
||||||
|
|
||||||
// remove duplicates
|
// remove duplicates
|
||||||
selectedAPIs = unique(selectedAPIs)
|
selectedAPIs = unique(selectedAPIs)
|
||||||
|
|
||||||
@ -51,7 +49,7 @@ func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpccl
|
|||||||
rpc.API{
|
rpc.API{
|
||||||
Namespace: EthNamespace,
|
Namespace: EthNamespace,
|
||||||
Version: apiVersion,
|
Version: apiVersion,
|
||||||
Service: ethAPI,
|
Service: eth.NewPublicAPI(ctx.Logger, clientCtx, evmBackend, nonceLock),
|
||||||
Public: true,
|
Public: true,
|
||||||
},
|
},
|
||||||
rpc.API{
|
rpc.API{
|
||||||
@ -84,7 +82,7 @@ func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpccl
|
|||||||
rpc.API{
|
rpc.API{
|
||||||
Namespace: PersonalNamespace,
|
Namespace: PersonalNamespace,
|
||||||
Version: apiVersion,
|
Version: apiVersion,
|
||||||
Service: personal.NewAPI(ctx.Logger, ethAPI),
|
Service: personal.NewAPI(ctx.Logger, clientCtx, evmBackend),
|
||||||
Public: true,
|
Public: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -111,7 +109,7 @@ func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpccl
|
|||||||
rpc.API{
|
rpc.API{
|
||||||
Namespace: MinerNamespace,
|
Namespace: MinerNamespace,
|
||||||
Version: apiVersion,
|
Version: apiVersion,
|
||||||
Service: miner.NewMinerAPI(ctx, ethAPI, evmBackend),
|
Service: miner.NewMinerAPI(ctx, clientCtx, evmBackend),
|
||||||
Public: true,
|
Public: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -2,11 +2,17 @@ package backend
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||||
|
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
@ -26,27 +32,22 @@ import (
|
|||||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Backend implements the functionality needed to filter changes.
|
// Backend implements the functionality shared within namespaces.
|
||||||
// Implemented by EVMBackend.
|
// Implemented by EVMBackend.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
// Used by block filter; also used for polling
|
|
||||||
BlockNumber() (hexutil.Uint64, error)
|
BlockNumber() (hexutil.Uint64, error)
|
||||||
HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, error)
|
|
||||||
HeaderByHash(blockHash common.Hash) (*ethtypes.Header, 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)
|
||||||
|
HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, error)
|
||||||
// returns the logs of a given block
|
HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error)
|
||||||
GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error)
|
|
||||||
|
|
||||||
// Used by pending transaction filter
|
|
||||||
PendingTransactions() ([]*sdk.Tx, error)
|
PendingTransactions() ([]*sdk.Tx, error)
|
||||||
|
|
||||||
// Used by log filter
|
|
||||||
GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error)
|
GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error)
|
||||||
|
GetTransactionCount(address common.Address, blockNum types.BlockNumber) (*hexutil.Uint64, error)
|
||||||
|
SendTransaction(args types.SendTxArgs) (common.Hash, error)
|
||||||
|
GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error)
|
||||||
BloomStatus() (uint64, uint64)
|
BloomStatus() (uint64, uint64)
|
||||||
|
|
||||||
GetCoinbase() (sdk.AccAddress, error)
|
GetCoinbase() (sdk.AccAddress, error)
|
||||||
|
EstimateGas(args evmtypes.CallArgs, blockNrOptional *types.BlockNumber) (hexutil.Uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Backend = (*EVMBackend)(nil)
|
var _ Backend = (*EVMBackend)(nil)
|
||||||
@ -444,3 +445,139 @@ func (e *EVMBackend) GetCoinbase() (sdk.AccAddress, error) {
|
|||||||
address, _ := sdk.AccAddressFromBech32(res.AccountAddress)
|
address, _ := sdk.AccAddressFromBech32(res.AccountAddress)
|
||||||
return address, nil
|
return address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EVMBackend) SendTransaction(args types.SendTxArgs) (common.Hash, error) {
|
||||||
|
// Look up the wallet containing the requested signer
|
||||||
|
_, err := e.clientCtx.Keyring.KeyByAddress(sdk.AccAddress(args.From.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Error("failed to find key in keyring", "address", args.From, "error", err.Error())
|
||||||
|
return common.Hash{}, fmt.Errorf("%s; %s", keystore.ErrNoMatch, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err = e.setTxDefaults(args)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := args.ToTransaction()
|
||||||
|
|
||||||
|
if err := msg.ValidateBasic(); err != nil {
|
||||||
|
e.logger.Debug("tx failed basic validation", "error", err.Error())
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: get from chain config
|
||||||
|
signer := ethtypes.LatestSignerForChainID(args.ChainID.ToInt())
|
||||||
|
|
||||||
|
// Sign transaction
|
||||||
|
if err := msg.Sign(signer, e.clientCtx.Keyring); err != nil {
|
||||||
|
e.logger.Debug("failed to sign tx", "error", err.Error())
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble transaction from fields
|
||||||
|
builder, ok := e.clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
|
||||||
|
if !ok {
|
||||||
|
e.logger.Error("clientCtx.TxConfig.NewTxBuilder returns unsupported builder", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{})
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Error("codectypes.NewAnyWithValue failed to pack an obvious value", "error", err.Error())
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.SetExtensionOptions(option)
|
||||||
|
err = builder.SetMsgs(msg)
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Error("builder.SetMsgs failed", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
txData, err := evmtypes.UnpackTxData(msg.Data)
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Error("failed to unpack tx data", "error", err.Error())
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fees := sdk.Coins{sdk.NewCoin(res.Params.EvmDenom, sdk.NewIntFromBigInt(txData.Fee()))}
|
||||||
|
builder.SetFeeAmount(fees)
|
||||||
|
builder.SetGasLimit(msg.GetGas())
|
||||||
|
|
||||||
|
// Encode transaction by default Tx encoder
|
||||||
|
txEncoder := e.clientCtx.TxConfig.TxEncoder()
|
||||||
|
txBytes, err := txEncoder(builder.GetTx())
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Error("failed to encode eth tx using default encoder", "error", err.Error())
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
txHash := msg.AsTransaction().Hash()
|
||||||
|
|
||||||
|
// Broadcast transaction in sync mode (default)
|
||||||
|
// NOTE: If error is encountered on the node, the broadcast will not return an error
|
||||||
|
syncCtx := e.clientCtx.WithBroadcastMode(flags.BroadcastSync)
|
||||||
|
rsp, err := syncCtx.BroadcastTx(txBytes)
|
||||||
|
if err != nil || rsp.Code != 0 {
|
||||||
|
if err == nil {
|
||||||
|
err = errors.New(rsp.RawLog)
|
||||||
|
}
|
||||||
|
e.logger.Error("failed to broadcast tx", "error", err.Error())
|
||||||
|
return txHash, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return transaction hash
|
||||||
|
return txHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EstimateGas returns an estimate of gas usage for the given smart contract call.
|
||||||
|
func (e *EVMBackend) EstimateGas(args evmtypes.CallArgs, blockNrOptional *types.BlockNumber) (hexutil.Uint64, error) {
|
||||||
|
blockNr := types.EthPendingBlockNumber
|
||||||
|
if blockNrOptional != nil {
|
||||||
|
blockNr = *blockNrOptional
|
||||||
|
}
|
||||||
|
|
||||||
|
bz, err := json.Marshal(&args)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
req := evmtypes.EthCallRequest{Args: bz, GasCap: ethermint.DefaultRPCGasLimit}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
res, err := e.queryClient.EstimateGas(types.ContextWithHeight(blockNr.Int64()), &req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return hexutil.Uint64(res.Gas), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
|
||||||
|
func (e *EVMBackend) GetTransactionCount(address common.Address, blockNum types.BlockNumber) (*hexutil.Uint64, error) {
|
||||||
|
// Get nonce (sequence) from account
|
||||||
|
from := sdk.AccAddress(address.Bytes())
|
||||||
|
accRet := e.clientCtx.AccountRetriever
|
||||||
|
|
||||||
|
err := accRet.EnsureExists(e.clientCtx, from)
|
||||||
|
if err != nil {
|
||||||
|
// account doesn't exist yet, return 0
|
||||||
|
n := hexutil.Uint64(0)
|
||||||
|
return &n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
includePending := blockNum == types.EthPendingBlockNumber
|
||||||
|
nonce, err := e.getAccountNonce(address, includePending, blockNum.Int64(), e.logger)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n := hexutil.Uint64(nonce)
|
||||||
|
return &n, nil
|
||||||
|
}
|
||||||
|
136
ethereum/rpc/backend/utils.go
Normal file
136
ethereum/rpc/backend/utils.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/tharsis/ethermint/ethereum/rpc/types"
|
||||||
|
ethermint "github.com/tharsis/ethermint/types"
|
||||||
|
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setTxDefaults populates tx message with default values in case they are not
|
||||||
|
// provided on the args
|
||||||
|
func (e *EVMBackend) setTxDefaults(args types.SendTxArgs) (types.SendTxArgs, error) {
|
||||||
|
|
||||||
|
if args.GasPrice == nil {
|
||||||
|
// TODO: Change to either:
|
||||||
|
// - min gas price from context once available through server/daemon, or
|
||||||
|
// - suggest a gas price based on the previous included txs
|
||||||
|
args.GasPrice = (*hexutil.Big)(big.NewInt(ethermint.DefaultGasPrice))
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Nonce == nil {
|
||||||
|
// get the nonce from the account retriever
|
||||||
|
// ignore error in case tge account doesn't exist yet
|
||||||
|
nonce, _ := e.getAccountNonce(args.From, true, 0, e.logger)
|
||||||
|
args.Nonce = (*hexutil.Uint64)(&nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
|
||||||
|
return args, errors.New("both 'data' and 'input' are set and not equal. Please use 'input' to pass transaction call data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.To == nil {
|
||||||
|
// Contract creation
|
||||||
|
var input []byte
|
||||||
|
if args.Data != nil {
|
||||||
|
input = *args.Data
|
||||||
|
} else if args.Input != nil {
|
||||||
|
input = *args.Input
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(input) == 0 {
|
||||||
|
return args, errors.New(`contract creation without any data provided`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Gas == nil {
|
||||||
|
// For backwards-compatibility reason, we try both input and data
|
||||||
|
// but input is preferred.
|
||||||
|
input := args.Input
|
||||||
|
if input == nil {
|
||||||
|
input = args.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
callArgs := evmtypes.CallArgs{
|
||||||
|
From: &args.From, // From shouldn't be nil
|
||||||
|
To: args.To,
|
||||||
|
Gas: args.Gas,
|
||||||
|
GasPrice: args.GasPrice,
|
||||||
|
Value: args.Value,
|
||||||
|
Data: input,
|
||||||
|
AccessList: args.AccessList,
|
||||||
|
}
|
||||||
|
blockNr := types.NewBlockNumber(big.NewInt(0))
|
||||||
|
estimated, err := e.EstimateGas(callArgs, &blockNr)
|
||||||
|
if err != nil {
|
||||||
|
return args, err
|
||||||
|
}
|
||||||
|
args.Gas = &estimated
|
||||||
|
e.logger.Debug("estimate gas usage automatically", "gas", args.Gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.ChainID == nil {
|
||||||
|
args.ChainID = (*hexutil.Big)(e.chainID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAccountNonce returns the account nonce for the given account address.
|
||||||
|
// If the pending value is true, it will iterate over the mempool (pending)
|
||||||
|
// txs in order to compute and return the pending tx sequence.
|
||||||
|
// Todo: include the ability to specify a blockNumber
|
||||||
|
func (e *EVMBackend) getAccountNonce(accAddr common.Address, pending bool, height int64, logger log.Logger) (uint64, error) {
|
||||||
|
queryClient := authtypes.NewQueryClient(e.clientCtx)
|
||||||
|
res, err := queryClient.Account(types.ContextWithHeight(height), &authtypes.QueryAccountRequest{Address: sdk.AccAddress(accAddr.Bytes()).String()})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var acc authtypes.AccountI
|
||||||
|
if err := e.clientCtx.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := acc.GetSequence()
|
||||||
|
|
||||||
|
if !pending {
|
||||||
|
return nonce, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// the account retriever doesn't include the uncommitted transactions on the nonce so we need to
|
||||||
|
// to manually add them.
|
||||||
|
pendingTxs, err := e.PendingTransactions()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to fetch pending transactions", "error", err.Error())
|
||||||
|
return nonce, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the uncommitted txs to the nonce counter
|
||||||
|
// only supports `MsgEthereumTx` style tx
|
||||||
|
for _, tx := range pendingTxs {
|
||||||
|
msg, err := evmtypes.UnwrapEthereumMsg(tx)
|
||||||
|
if err != nil {
|
||||||
|
// not ethereum tx
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sender, err := msg.GetSender(e.chainID)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if sender == accAddr {
|
||||||
|
nonce++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonce, nil
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -240,26 +239,7 @@ func (e *PublicAPI) GetStorageAt(address common.Address, key string, blockNum rp
|
|||||||
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
|
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
|
||||||
func (e *PublicAPI) GetTransactionCount(address common.Address, blockNum rpctypes.BlockNumber) (*hexutil.Uint64, error) {
|
func (e *PublicAPI) GetTransactionCount(address common.Address, blockNum rpctypes.BlockNumber) (*hexutil.Uint64, error) {
|
||||||
e.logger.Debug("eth_getTransactionCount", "address", address.Hex(), "block number", blockNum)
|
e.logger.Debug("eth_getTransactionCount", "address", address.Hex(), "block number", blockNum)
|
||||||
|
return e.backend.GetTransactionCount(address, blockNum)
|
||||||
// Get nonce (sequence) from account
|
|
||||||
from := sdk.AccAddress(address.Bytes())
|
|
||||||
accRet := e.clientCtx.AccountRetriever
|
|
||||||
|
|
||||||
err := accRet.EnsureExists(e.clientCtx, from)
|
|
||||||
if err != nil {
|
|
||||||
// account doesn't exist yet, return 0
|
|
||||||
n := hexutil.Uint64(0)
|
|
||||||
return &n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
includePending := blockNum == rpctypes.EthPendingBlockNumber
|
|
||||||
nonce, err := e.getAccountNonce(address, includePending, blockNum.Int64(), e.logger)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n := hexutil.Uint64(nonce)
|
|
||||||
return &n, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash.
|
// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash.
|
||||||
@ -357,94 +337,7 @@ func (e *PublicAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.By
|
|||||||
// SendTransaction sends an Ethereum transaction.
|
// SendTransaction sends an Ethereum transaction.
|
||||||
func (e *PublicAPI) SendTransaction(args rpctypes.SendTxArgs) (common.Hash, error) {
|
func (e *PublicAPI) SendTransaction(args rpctypes.SendTxArgs) (common.Hash, error) {
|
||||||
e.logger.Debug("eth_sendTransaction", "args", args.String())
|
e.logger.Debug("eth_sendTransaction", "args", args.String())
|
||||||
|
return e.backend.SendTransaction(args)
|
||||||
// Look up the wallet containing the requested signer
|
|
||||||
_, err := e.clientCtx.Keyring.KeyByAddress(sdk.AccAddress(args.From.Bytes()))
|
|
||||||
if err != nil {
|
|
||||||
e.logger.Error("failed to find key in keyring", "address", args.From, "error", err.Error())
|
|
||||||
return common.Hash{}, fmt.Errorf("%s; %s", keystore.ErrNoMatch, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
args, err = e.setTxDefaults(args)
|
|
||||||
if err != nil {
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := args.ToTransaction()
|
|
||||||
|
|
||||||
if err := msg.ValidateBasic(); err != nil {
|
|
||||||
e.logger.Debug("tx failed basic validation", "error", err.Error())
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: get from chain config
|
|
||||||
signer := ethtypes.LatestSignerForChainID(args.ChainID.ToInt())
|
|
||||||
|
|
||||||
// Sign transaction
|
|
||||||
if err := msg.Sign(signer, e.clientCtx.Keyring); err != nil {
|
|
||||||
e.logger.Debug("failed to sign tx", "error", err.Error())
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble transaction from fields
|
|
||||||
builder, ok := e.clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
|
|
||||||
if !ok {
|
|
||||||
e.logger.Error("clientCtx.TxConfig.NewTxBuilder returns unsupported builder", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{})
|
|
||||||
if err != nil {
|
|
||||||
e.logger.Error("codectypes.NewAnyWithValue failed to pack an obvious value", "error", err.Error())
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.SetExtensionOptions(option)
|
|
||||||
err = builder.SetMsgs(msg)
|
|
||||||
if err != nil {
|
|
||||||
e.logger.Error("builder.SetMsgs failed", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
txData, err := evmtypes.UnpackTxData(msg.Data)
|
|
||||||
if err != nil {
|
|
||||||
e.logger.Error("failed to unpack tx data", "error", err.Error())
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fees := sdk.Coins{sdk.NewCoin(res.Params.EvmDenom, sdk.NewIntFromBigInt(txData.Fee()))}
|
|
||||||
builder.SetFeeAmount(fees)
|
|
||||||
builder.SetGasLimit(msg.GetGas())
|
|
||||||
|
|
||||||
// Encode transaction by default Tx encoder
|
|
||||||
txEncoder := e.clientCtx.TxConfig.TxEncoder()
|
|
||||||
txBytes, err := txEncoder(builder.GetTx())
|
|
||||||
if err != nil {
|
|
||||||
e.logger.Error("failed to encode eth tx using default encoder", "error", err.Error())
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
txHash := msg.AsTransaction().Hash()
|
|
||||||
|
|
||||||
// Broadcast transaction in sync mode (default)
|
|
||||||
// NOTE: If error is encountered on the node, the broadcast will not return an error
|
|
||||||
syncCtx := e.clientCtx.WithBroadcastMode(flags.BroadcastSync)
|
|
||||||
rsp, err := syncCtx.BroadcastTx(txBytes)
|
|
||||||
if err != nil || rsp.Code != 0 {
|
|
||||||
if err == nil {
|
|
||||||
err = errors.New(rsp.RawLog)
|
|
||||||
}
|
|
||||||
e.logger.Error("failed to broadcast tx", "error", err.Error())
|
|
||||||
return txHash, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return transaction hash
|
|
||||||
return txHash, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRawTransaction send a raw Ethereum transaction.
|
// SendRawTransaction send a raw Ethereum transaction.
|
||||||
@ -568,26 +461,7 @@ func (e *PublicAPI) doCall(
|
|||||||
// EstimateGas returns an estimate of gas usage for the given smart contract call.
|
// EstimateGas returns an estimate of gas usage for the given smart contract call.
|
||||||
func (e *PublicAPI) EstimateGas(args evmtypes.CallArgs, blockNrOptional *rpctypes.BlockNumber) (hexutil.Uint64, error) {
|
func (e *PublicAPI) EstimateGas(args evmtypes.CallArgs, blockNrOptional *rpctypes.BlockNumber) (hexutil.Uint64, error) {
|
||||||
e.logger.Debug("eth_estimateGas")
|
e.logger.Debug("eth_estimateGas")
|
||||||
|
return e.backend.EstimateGas(args, blockNrOptional)
|
||||||
blockNr := rpctypes.EthPendingBlockNumber
|
|
||||||
if blockNrOptional != nil {
|
|
||||||
blockNr = *blockNrOptional
|
|
||||||
}
|
|
||||||
|
|
||||||
bz, err := json.Marshal(&args)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
req := evmtypes.EthCallRequest{Args: bz, GasCap: ethermint.DefaultRPCGasLimit}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
res, err := e.queryClient.EstimateGas(rpctypes.ContextWithHeight(blockNr.Int64()), &req)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return hexutil.Uint64(res.Gas), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockByHash returns the block identified by hash.
|
// GetBlockByHash returns the block identified by hash.
|
||||||
@ -982,122 +856,3 @@ func (e *PublicAPI) GetProof(address common.Address, storageKeys []string, block
|
|||||||
StorageProof: storageProofs,
|
StorageProof: storageProofs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setTxDefaults populates tx message with default values in case they are not
|
|
||||||
// provided on the args
|
|
||||||
func (e *PublicAPI) setTxDefaults(args rpctypes.SendTxArgs) (rpctypes.SendTxArgs, error) {
|
|
||||||
|
|
||||||
if args.GasPrice == nil {
|
|
||||||
// TODO: Change to either:
|
|
||||||
// - min gas price from context once available through server/daemon, or
|
|
||||||
// - suggest a gas price based on the previous included txs
|
|
||||||
args.GasPrice = (*hexutil.Big)(big.NewInt(ethermint.DefaultGasPrice))
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Nonce == nil {
|
|
||||||
// get the nonce from the account retriever
|
|
||||||
// ignore error in case tge account doesn't exist yet
|
|
||||||
nonce, _ := e.getAccountNonce(args.From, true, 0, e.logger)
|
|
||||||
args.Nonce = (*hexutil.Uint64)(&nonce)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
|
|
||||||
return args, errors.New("both 'data' and 'input' are set and not equal. Please use 'input' to pass transaction call data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.To == nil {
|
|
||||||
// Contract creation
|
|
||||||
var input []byte
|
|
||||||
if args.Data != nil {
|
|
||||||
input = *args.Data
|
|
||||||
} else if args.Input != nil {
|
|
||||||
input = *args.Input
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(input) == 0 {
|
|
||||||
return args, errors.New(`contract creation without any data provided`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Gas == nil {
|
|
||||||
// For backwards-compatibility reason, we try both input and data
|
|
||||||
// but input is preferred.
|
|
||||||
input := args.Input
|
|
||||||
if input == nil {
|
|
||||||
input = args.Data
|
|
||||||
}
|
|
||||||
|
|
||||||
callArgs := evmtypes.CallArgs{
|
|
||||||
From: &args.From, // From shouldn't be nil
|
|
||||||
To: args.To,
|
|
||||||
Gas: args.Gas,
|
|
||||||
GasPrice: args.GasPrice,
|
|
||||||
Value: args.Value,
|
|
||||||
Data: input,
|
|
||||||
AccessList: args.AccessList,
|
|
||||||
}
|
|
||||||
blockNr := rpctypes.NewBlockNumber(big.NewInt(0))
|
|
||||||
estimated, err := e.EstimateGas(callArgs, &blockNr)
|
|
||||||
if err != nil {
|
|
||||||
return args, err
|
|
||||||
}
|
|
||||||
args.Gas = &estimated
|
|
||||||
e.logger.Debug("estimate gas usage automatically", "gas", args.Gas)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.ChainID == nil {
|
|
||||||
args.ChainID = (*hexutil.Big)(e.chainIDEpoch)
|
|
||||||
}
|
|
||||||
|
|
||||||
return args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAccountNonce returns the account nonce for the given account address.
|
|
||||||
// If the pending value is true, it will iterate over the mempool (pending)
|
|
||||||
// txs in order to compute and return the pending tx sequence.
|
|
||||||
// Todo: include the ability to specify a blockNumber
|
|
||||||
func (e *PublicAPI) getAccountNonce(accAddr common.Address, pending bool, height int64, logger log.Logger) (uint64, error) {
|
|
||||||
queryClient := authtypes.NewQueryClient(e.clientCtx)
|
|
||||||
res, err := queryClient.Account(rpctypes.ContextWithHeight(height), &authtypes.QueryAccountRequest{Address: sdk.AccAddress(accAddr.Bytes()).String()})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
var acc authtypes.AccountI
|
|
||||||
if err := e.clientCtx.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce := acc.GetSequence()
|
|
||||||
|
|
||||||
if !pending {
|
|
||||||
return nonce, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// the account retriever doesn't include the uncommitted transactions on the nonce so we need to
|
|
||||||
// to manually add them.
|
|
||||||
pendingTxs, err := e.backend.PendingTransactions()
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("failed to fetch pending transactions", "error", err.Error())
|
|
||||||
return nonce, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the uncommitted txs to the nonce counter
|
|
||||||
// only supports `MsgEthereumTx` style tx
|
|
||||||
for _, tx := range pendingTxs {
|
|
||||||
msg, err := evmtypes.UnwrapEthereumMsg(tx)
|
|
||||||
if err != nil {
|
|
||||||
// not ethereum tx
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
sender, err := msg.GetSender(e.chainIDEpoch)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if sender == accAddr {
|
|
||||||
nonce++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nonce, nil
|
|
||||||
}
|
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
@ -20,7 +22,6 @@ import (
|
|||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
"github.com/tharsis/ethermint/ethereum/rpc/backend"
|
"github.com/tharsis/ethermint/ethereum/rpc/backend"
|
||||||
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth"
|
|
||||||
rpctypes "github.com/tharsis/ethermint/ethereum/rpc/types"
|
rpctypes "github.com/tharsis/ethermint/ethereum/rpc/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,19 +29,19 @@ import (
|
|||||||
type API struct {
|
type API struct {
|
||||||
ctx *server.Context
|
ctx *server.Context
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
ethAPI *eth.PublicAPI
|
clientCtx client.Context
|
||||||
backend backend.Backend
|
backend backend.Backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMinerAPI creates an instance of the Miner API.
|
// NewMinerAPI creates an instance of the Miner API.
|
||||||
func NewMinerAPI(
|
func NewMinerAPI(
|
||||||
ctx *server.Context,
|
ctx *server.Context,
|
||||||
ethAPI *eth.PublicAPI,
|
clientCtx client.Context,
|
||||||
backend backend.Backend,
|
backend backend.Backend,
|
||||||
) *API {
|
) *API {
|
||||||
return &API{
|
return &API{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
ethAPI: ethAPI,
|
clientCtx: clientCtx,
|
||||||
logger: ctx.Logger.With("api", "miner"),
|
logger: ctx.Logger.With("api", "miner"),
|
||||||
backend: backend,
|
backend: backend,
|
||||||
}
|
}
|
||||||
@ -65,7 +66,7 @@ func (api *API) SetEtherbase(etherbase common.Address) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assemble transaction from fields
|
// Assemble transaction from fields
|
||||||
builder, ok := api.ethAPI.ClientCtx().TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
|
builder, ok := api.clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
|
||||||
if !ok {
|
if !ok {
|
||||||
api.logger.Debug("clientCtx.TxConfig.NewTxBuilder returns unsupported builder", "error", err.Error())
|
api.logger.Debug("clientCtx.TxConfig.NewTxBuilder returns unsupported builder", "error", err.Error())
|
||||||
return false
|
return false
|
||||||
@ -93,7 +94,7 @@ func (api *API) SetEtherbase(etherbase common.Address) bool {
|
|||||||
denom := minGasPrices[0].Denom
|
denom := minGasPrices[0].Denom
|
||||||
|
|
||||||
delCommonAddr := common.BytesToAddress(delAddr.Bytes())
|
delCommonAddr := common.BytesToAddress(delAddr.Bytes())
|
||||||
nonce, err := api.ethAPI.GetTransactionCount(delCommonAddr, rpctypes.EthPendingBlockNumber)
|
nonce, err := api.backend.GetTransactionCount(delCommonAddr, rpctypes.EthPendingBlockNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.logger.Debug("failed to get nonce", "error", err.Error())
|
api.logger.Debug("failed to get nonce", "error", err.Error())
|
||||||
return false
|
return false
|
||||||
@ -101,13 +102,13 @@ func (api *API) SetEtherbase(etherbase common.Address) bool {
|
|||||||
|
|
||||||
txFactory := tx.Factory{}
|
txFactory := tx.Factory{}
|
||||||
txFactory = txFactory.
|
txFactory = txFactory.
|
||||||
WithChainID(api.ethAPI.ClientCtx().ChainID).
|
WithChainID(api.clientCtx.ChainID).
|
||||||
WithKeybase(api.ethAPI.ClientCtx().Keyring).
|
WithKeybase(api.clientCtx.Keyring).
|
||||||
WithTxConfig(api.ethAPI.ClientCtx().TxConfig).
|
WithTxConfig(api.clientCtx.TxConfig).
|
||||||
WithSequence(uint64(*nonce)).
|
WithSequence(uint64(*nonce)).
|
||||||
WithGasAdjustment(1.25)
|
WithGasAdjustment(1.25)
|
||||||
|
|
||||||
_, gas, err := tx.CalculateGas(api.ethAPI.ClientCtx(), txFactory, msg)
|
_, gas, err := tx.CalculateGas(api.clientCtx, txFactory, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.logger.Debug("failed to calculate gas", "error", err.Error())
|
api.logger.Debug("failed to calculate gas", "error", err.Error())
|
||||||
return false
|
return false
|
||||||
@ -120,7 +121,7 @@ func (api *API) SetEtherbase(etherbase common.Address) bool {
|
|||||||
builder.SetFeeAmount(fees)
|
builder.SetFeeAmount(fees)
|
||||||
builder.SetGasLimit(gas)
|
builder.SetGasLimit(gas)
|
||||||
|
|
||||||
keyInfo, err := api.ethAPI.ClientCtx().Keyring.KeyByAddress(delAddr)
|
keyInfo, err := api.clientCtx.Keyring.KeyByAddress(delAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.logger.Debug("failed to get the wallet address using the keyring", "error", err.Error())
|
api.logger.Debug("failed to get the wallet address using the keyring", "error", err.Error())
|
||||||
return false
|
return false
|
||||||
@ -132,7 +133,7 @@ func (api *API) SetEtherbase(etherbase common.Address) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encode transaction by default Tx encoder
|
// Encode transaction by default Tx encoder
|
||||||
txEncoder := api.ethAPI.ClientCtx().TxConfig.TxEncoder()
|
txEncoder := api.clientCtx.TxConfig.TxEncoder()
|
||||||
txBytes, err := txEncoder(builder.GetTx())
|
txBytes, err := txEncoder(builder.GetTx())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.logger.Debug("failed to encode eth tx using default encoder", "error", err.Error())
|
api.logger.Debug("failed to encode eth tx using default encoder", "error", err.Error())
|
||||||
@ -143,7 +144,7 @@ func (api *API) SetEtherbase(etherbase common.Address) bool {
|
|||||||
|
|
||||||
// Broadcast transaction in sync mode (default)
|
// Broadcast transaction in sync mode (default)
|
||||||
// NOTE: If error is encountered on the node, the broadcast will not return an error
|
// NOTE: If error is encountered on the node, the broadcast will not return an error
|
||||||
syncCtx := api.ethAPI.ClientCtx().WithBroadcastMode(flags.BroadcastSync)
|
syncCtx := api.clientCtx.WithBroadcastMode(flags.BroadcastSync)
|
||||||
rsp, err := syncCtx.BroadcastTx(txBytes)
|
rsp, err := syncCtx.BroadcastTx(txBytes)
|
||||||
if err != nil || rsp.Code != 0 {
|
if err != nil || rsp.Code != 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -6,6 +6,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tharsis/ethermint/ethereum/rpc/backend"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
|
||||||
"github.com/tharsis/ethermint/crypto/hd"
|
"github.com/tharsis/ethermint/crypto/hd"
|
||||||
ethermint "github.com/tharsis/ethermint/types"
|
ethermint "github.com/tharsis/ethermint/types"
|
||||||
|
|
||||||
@ -21,19 +25,19 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
||||||
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
||||||
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth"
|
|
||||||
rpctypes "github.com/tharsis/ethermint/ethereum/rpc/types"
|
rpctypes "github.com/tharsis/ethermint/ethereum/rpc/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrivateAccountAPI is the personal_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
// PrivateAccountAPI is the personal_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
||||||
type PrivateAccountAPI struct {
|
type PrivateAccountAPI struct {
|
||||||
ethAPI *eth.PublicAPI
|
clientCtx client.Context
|
||||||
|
backend backend.Backend
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
hdPathIter ethermint.HDPathIterator
|
hdPathIter ethermint.HDPathIterator
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAPI creates an instance of the public Personal Eth API.
|
// NewAPI creates an instance of the public Personal Eth API.
|
||||||
func NewAPI(logger log.Logger, ethAPI *eth.PublicAPI) *PrivateAccountAPI {
|
func NewAPI(logger log.Logger, clientCtx client.Context, backend backend.Backend) *PrivateAccountAPI {
|
||||||
cfg := sdk.GetConfig()
|
cfg := sdk.GetConfig()
|
||||||
basePath := cfg.GetFullFundraiserPath()
|
basePath := cfg.GetFullFundraiserPath()
|
||||||
|
|
||||||
@ -43,9 +47,10 @@ func NewAPI(logger log.Logger, ethAPI *eth.PublicAPI) *PrivateAccountAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &PrivateAccountAPI{
|
return &PrivateAccountAPI{
|
||||||
ethAPI: ethAPI,
|
clientCtx: clientCtx,
|
||||||
logger: logger.With("api", "personal"),
|
logger: logger.With("api", "personal"),
|
||||||
hdPathIter: iterator,
|
hdPathIter: iterator,
|
||||||
|
backend: backend,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,17 +72,17 @@ func (api *PrivateAccountAPI) ImportRawKey(privkey, password string) (common.Add
|
|||||||
ethereumAddr := common.BytesToAddress(addr)
|
ethereumAddr := common.BytesToAddress(addr)
|
||||||
|
|
||||||
// return if the key has already been imported
|
// return if the key has already been imported
|
||||||
if _, err := api.ethAPI.ClientCtx().Keyring.KeyByAddress(addr); err == nil {
|
if _, err := api.clientCtx.Keyring.KeyByAddress(addr); err == nil {
|
||||||
return ethereumAddr, nil
|
return ethereumAddr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore error as we only care about the length of the list
|
// ignore error as we only care about the length of the list
|
||||||
list, _ := api.ethAPI.ClientCtx().Keyring.List()
|
list, _ := api.clientCtx.Keyring.List()
|
||||||
privKeyName := fmt.Sprintf("personal_%d", len(list))
|
privKeyName := fmt.Sprintf("personal_%d", len(list))
|
||||||
|
|
||||||
armor := sdkcrypto.EncryptArmorPrivKey(privKey, password, ethsecp256k1.KeyType)
|
armor := sdkcrypto.EncryptArmorPrivKey(privKey, password, ethsecp256k1.KeyType)
|
||||||
|
|
||||||
if err := api.ethAPI.ClientCtx().Keyring.ImportPrivKey(privKeyName, armor, password); err != nil {
|
if err := api.clientCtx.Keyring.ImportPrivKey(privKeyName, armor, password); err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +96,7 @@ func (api *PrivateAccountAPI) ListAccounts() ([]common.Address, error) {
|
|||||||
api.logger.Debug("personal_listAccounts")
|
api.logger.Debug("personal_listAccounts")
|
||||||
addrs := []common.Address{}
|
addrs := []common.Address{}
|
||||||
|
|
||||||
list, err := api.ethAPI.ClientCtx().Keyring.List()
|
list, err := api.clientCtx.Keyring.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -121,7 +126,7 @@ func (api *PrivateAccountAPI) NewAccount(password string) (common.Address, error
|
|||||||
// create the mnemonic and save the account
|
// create the mnemonic and save the account
|
||||||
hdPath := api.hdPathIter()
|
hdPath := api.hdPathIter()
|
||||||
|
|
||||||
info, _, err := api.ethAPI.ClientCtx().Keyring.NewMnemonic(name, keyring.English, hdPath.String(), password, hd.EthSecp256k1)
|
info, _, err := api.clientCtx.Keyring.NewMnemonic(name, keyring.English, hdPath.String(), password, hd.EthSecp256k1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
@ -146,8 +151,8 @@ func (api *PrivateAccountAPI) UnlockAccount(_ context.Context, addr common.Addre
|
|||||||
// 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 rpctypes.SendTxArgs, pwrd string) (common.Hash, error) {
|
func (api *PrivateAccountAPI) SendTransaction(_ context.Context, args rpctypes.SendTxArgs, pwrd string) (common.Hash, error) {
|
||||||
|
api.logger.Debug("personal_sendTransaction", "address", args.To.String())
|
||||||
return api.ethAPI.SendTransaction(args)
|
return api.backend.SendTransaction(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign calculates an Ethereum ECDSA signature for:
|
// Sign calculates an Ethereum ECDSA signature for:
|
||||||
@ -164,7 +169,7 @@ func (api *PrivateAccountAPI) Sign(_ context.Context, data hexutil.Bytes, addr c
|
|||||||
|
|
||||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||||
|
|
||||||
sig, _, err := api.ethAPI.ClientCtx().Keyring.SignByAddress(cosmosAddr, accounts.TextHash(data))
|
sig, _, err := api.clientCtx.Keyring.SignByAddress(cosmosAddr, accounts.TextHash(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.logger.Error("failed to sign with key", "data", data, "address", addr.String(), "error", err.Error())
|
api.logger.Error("failed to sign with key", "data", data, "address", addr.String(), "error", err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user