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:
parent
40b3b9ae1b
commit
f70e4c1253
@ -43,6 +43,8 @@ import (
|
|||||||
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
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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)")
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user