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. // Implemented by EVMBackend.
type Backend interface { type Backend interface {
// General Ethereum API // 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 RPCMinGasPrice() int64
SuggestGasTipCap() (*big.Int, error) SuggestGasTipCap() (*big.Int, error)
@ -779,6 +781,11 @@ func (e *EVMBackend) RPCGasCap() uint64 {
return e.cfg.JSONRPC.GasCap 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 // RPCFilterCap is the limit for total number of filters that can be created
func (e *EVMBackend) RPCFilterCap() int32 { func (e *EVMBackend) RPCFilterCap() int32 {
return e.cfg.JSONRPC.FilterCap return e.cfg.JSONRPC.FilterCap

View File

@ -9,6 +9,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -416,6 +417,7 @@ func (e *PublicAPI) FillTransaction(args evmtypes.TransactionArgs) (*rpctypes.Si
// Assemble the transaction and obtain rlp // Assemble the transaction and obtain rlp
tx := args.ToTransaction().AsTransaction() tx := args.ToTransaction().AsTransaction()
data, err := tx.MarshalBinary() data, err := tx.MarshalBinary()
if err != nil { if err != nil {
return nil, err return nil, err
@ -510,6 +512,26 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
return txHash, nil 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 // 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. // 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) { 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() 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() pending, err := e.backend.PendingTransactions()
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err

View File

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

View File

@ -31,6 +31,7 @@ const (
JSONRPCAddress = "json-rpc.address" JSONRPCAddress = "json-rpc.address"
JSONWsAddress = "json-rpc.ws-address" JSONWsAddress = "json-rpc.ws-address"
JSONRPCGasCap = "json-rpc.gas-cap" JSONRPCGasCap = "json-rpc.gas-cap"
JSONRPCTxFeeCap = "json-rpc.txfee-cap"
JSONRPCFilterCap = "json-rpc.filter-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.Address, "tcp://0.0.0.0:26658", "Listen address")
cmd.Flags().String(srvflags.Transport, "socket", "Transport protocol: socket, grpc") 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(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().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.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") 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().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.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().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().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)") 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) { func TestEth_GetProof(t *testing.T) {
rpcRes := call(t, "eth_sendTransaction", makeEthTxParam()) rpcRes := call(t, "eth_sendTransaction", makeEthTxParam())
var hash hexutil.Bytes var hash hexutil.Bytes
@ -401,7 +400,6 @@ func TestEth_GetCode(t *testing.T) {
} }
func TestEth_SendTransaction_Transfer(t *testing.T) { func TestEth_SendTransaction_Transfer(t *testing.T) {
rpcRes := call(t, "eth_sendTransaction", makeEthTxParam()) rpcRes := call(t, "eth_sendTransaction", makeEthTxParam())
var hash hexutil.Bytes var hash hexutil.Bytes
@ -1027,3 +1025,16 @@ func makeEthTxParam() []map[string]string {
return param 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))
}