rpc: eth_resend (#684)

* Problem: missing json rpc for eth_resend #675

add unit test

restore

Update server/start.go

thanks~

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

tidy up checkTxFee

change comments

* fix lint

* Apply suggestions from code review

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
Jongwhan Lee 2021-10-21 04:14:39 +09:00 committed by GitHub
parent 40b3b9ae1b
commit f70e4c1253
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 5 deletions

View File

@ -42,7 +42,9 @@ import (
// Implemented by EVMBackend.
type Backend interface {
// General Ethereum API
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
RPCTxFeeCap() float64 // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for, // send-transction variants. The unit is ether.
RPCMinGasPrice() int64
SuggestGasTipCap() (*big.Int, error)
@ -779,6 +781,11 @@ func (e *EVMBackend) RPCGasCap() uint64 {
return e.cfg.JSONRPC.GasCap
}
// RPCGasCap is the global gas cap for eth-call variants.
func (e *EVMBackend) RPCTxFeeCap() float64 {
return e.cfg.JSONRPC.TxFeeCap
}
// RPCFilterCap is the limit for total number of filters that can be created
func (e *EVMBackend) RPCFilterCap() int32 {
return e.cfg.JSONRPC.FilterCap

View File

@ -9,6 +9,7 @@ import (
"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"
@ -416,6 +417,7 @@ func (e *PublicAPI) FillTransaction(args evmtypes.TransactionArgs) (*rpctypes.Si
// Assemble the transaction and obtain rlp
tx := args.ToTransaction().AsTransaction()
data, err := tx.MarshalBinary()
if err != nil {
return nil, err
@ -510,6 +512,26 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
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) {
@ -525,6 +547,19 @@ func (e *PublicAPI) Resend(ctx context.Context, args evmtypes.TransactionArgs, g
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

View File

@ -30,6 +30,9 @@ const (
DefaultGasCap uint64 = 25000000
DefaultFilterCap int32 = 200
// default 1.0 eth
DefaultTxFeeCap float64 = 1.0
)
var evmTracers = []string{DefaultEVMTracer, "markdown", "struct", "access_list"}
@ -61,6 +64,8 @@ type JSONRPCConfig struct {
WsAddress string `mapstructure:"ws-address"`
// GasCap is the global gas cap for eth-call variants.
GasCap uint64 `mapstructure:"gas-cap"`
// TxFeeCap is the global tx-fee cap for send transaction
TxFeeCap float64 `mapstructure:"txfee-cap"`
// FilterCap is the global cap for total number of filters that can be created.
FilterCap int32 `mapstructure:"filter-cap"`
// Enable defines if the EVM RPC server should be enabled.

View File

@ -31,6 +31,7 @@ const (
JSONRPCAddress = "json-rpc.address"
JSONWsAddress = "json-rpc.ws-address"
JSONRPCGasCap = "json-rpc.gas-cap"
JSONRPCTxFeeCap = "json-rpc.txfee-cap"
JSONRPCFilterCap = "json-rpc.filter-cap"
)

View File

@ -132,7 +132,7 @@ which accepts a path for the resulting pprof file.
cmd.Flags().String(srvflags.Address, "tcp://0.0.0.0:26658", "Listen address")
cmd.Flags().String(srvflags.Transport, "socket", "Transport protocol: socket, grpc")
cmd.Flags().String(srvflags.TraceStore, "", "Enable KVStore tracing to an output file")
cmd.Flags().String(server.FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)")
cmd.Flags().String(server.FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photon;0.0001stake)")
cmd.Flags().IntSlice(server.FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary")
cmd.Flags().Uint64(server.FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node")
cmd.Flags().Uint64(server.FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node")
@ -155,7 +155,8 @@ which accepts a path for the resulting pprof file.
cmd.Flags().StringSlice(srvflags.JSONRPCAPI, config.GetDefaultAPINamespaces(), "Defines a list of JSON-RPC namespaces that should be enabled")
cmd.Flags().String(srvflags.JSONRPCAddress, config.DefaultJSONRPCAddress, "the JSON-RPC server address to listen on")
cmd.Flags().String(srvflags.JSONWsAddress, config.DefaultJSONRPCWsAddress, "the JSON-RPC WS server address to listen on")
cmd.Flags().Uint64(srvflags.JSONRPCGasCap, config.DefaultGasCap, "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)")
cmd.Flags().Uint64(srvflags.JSONRPCGasCap, config.DefaultGasCap, "Sets a cap on gas that can be used in eth_call/estimateGas unit is aphoton (0=infinite)")
cmd.Flags().Float64(srvflags.JSONRPCTxFeeCap, config.DefaultTxFeeCap, "Sets a cap on transaction fee that can be sent via the RPC APIs (1 = default 1 photon)")
cmd.Flags().Int32(srvflags.JSONRPCFilterCap, config.DefaultFilterCap, "Sets the global cap for total number of filters that can be created")
cmd.Flags().String(srvflags.EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)")

View File

@ -360,7 +360,6 @@ func TestEth_GetStorageAt(t *testing.T) {
}
func TestEth_GetProof(t *testing.T) {
rpcRes := call(t, "eth_sendTransaction", makeEthTxParam())
var hash hexutil.Bytes
@ -401,7 +400,6 @@ func TestEth_GetCode(t *testing.T) {
}
func TestEth_SendTransaction_Transfer(t *testing.T) {
rpcRes := call(t, "eth_sendTransaction", makeEthTxParam())
var hash hexutil.Bytes
@ -1027,3 +1025,16 @@ func makeEthTxParam() []map[string]string {
return param
}
func TestEth_EthResend(t *testing.T) {
tx := make(map[string]string)
tx["from"] = "0x" + fmt.Sprintf("%x", from)
tx["to"] = "0x0000000000000000000000000000000012341234"
tx["value"] = "0x16345785d8a0000"
tx["nonce"] = "0x2"
tx["gasLimit"] = "0x5208"
tx["gasPrice"] = "0x55ae82600"
param := []interface{}{tx, "0x1", "0x2"}
_, rpcerror := callWithError("eth_resend", param)
require.Equal(t, "transaction 0x3bf28b46ee1bb3925e50ec6003f899f95913db4b0f579c4e7e887efebf9ecd1b not found", fmt.Sprintf("%s", rpcerror))
}