diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 4fa24cc1c..b0316c540 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -199,6 +199,7 @@ var ( utils.InsecureUnlockAllowedFlag, utils.RPCGlobalGasCapFlag, utils.RPCGlobalTxFeeCapFlag, + utils.AllowUnprotectedTxs, } whisperFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index ba643efcc..070b3a1de 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -593,6 +593,10 @@ var ( Name: "preload", Usage: "Comma separated list of JavaScript files to preload into the console", } + AllowUnprotectedTxs = cli.BoolFlag{ + Name: "rpc.allow-unprotected-txs", + Usage: "Allow for unprotected (non EIP155 signed) transactions to be submitted via RPC", + } // Network Settings MaxPeersFlag = cli.IntFlag{ @@ -966,6 +970,9 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(HTTPPathPrefixFlag.Name) { cfg.HTTPPathPrefix = ctx.GlobalString(HTTPPathPrefixFlag.Name) } + if ctx.GlobalIsSet(AllowUnprotectedTxs.Name) { + cfg.AllowUnprotectedTxs = ctx.GlobalBool(AllowUnprotectedTxs.Name) + } } // setGraphQL creates the GraphQL listener interface string from the set diff --git a/eth/api_backend.go b/eth/api_backend.go index 17de83a28..2569972e5 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -41,9 +41,10 @@ import ( // EthAPIBackend implements ethapi.Backend for full nodes type EthAPIBackend struct { - extRPCEnabled bool - eth *Ethereum - gpo *gasprice.Oracle + extRPCEnabled bool + allowUnprotectedTxs bool + eth *Ethereum + gpo *gasprice.Oracle } // ChainConfig returns the active chain configuration. @@ -292,6 +293,10 @@ func (b *EthAPIBackend) ExtRPCEnabled() bool { return b.extRPCEnabled } +func (b *EthAPIBackend) UnprotectedAllowed() bool { + return b.allowUnprotectedTxs +} + func (b *EthAPIBackend) RPCGasCap() uint64 { return b.eth.config.RPCGasCap } diff --git a/eth/backend.go b/eth/backend.go index 4170ecc94..044422763 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -222,7 +222,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) - eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), eth, nil} + eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} + if eth.APIBackend.allowUnprotectedTxs { + log.Info("Unprotected transactions allowed") + } gpoParams := config.GPO if gpoParams.Default == nil { gpoParams.Default = config.Miner.GasPrice diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d3c007b9b..52b4f5f50 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1555,6 +1555,10 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c if err := checkTxFee(tx.GasPrice(), tx.Gas(), b.RPCTxFeeCap()); err != nil { return common.Hash{}, err } + if !b.UnprotectedAllowed() && !tx.Protected() { + // Ensure only eip155 signed transactions are submitted if EIP155Required is set. + return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC") + } if err := b.SendTx(ctx, tx); err != nil { return common.Hash{}, err } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index f0a4c0493..ebb088fef 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -45,8 +45,9 @@ type Backend interface { ChainDb() ethdb.Database AccountManager() *accounts.Manager ExtRPCEnabled() bool - RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection - RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs + RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection + RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs + UnprotectedAllowed() bool // allows only for EIP155 transactions. // Blockchain API SetHead(number uint64) diff --git a/les/api_backend.go b/les/api_backend.go index 083961490..f5d2354b6 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -40,9 +40,10 @@ import ( ) type LesApiBackend struct { - extRPCEnabled bool - eth *LightEthereum - gpo *gasprice.Oracle + extRPCEnabled bool + allowUnprotectedTxs bool + eth *LightEthereum + gpo *gasprice.Oracle } func (b *LesApiBackend) ChainConfig() *params.ChainConfig { @@ -263,6 +264,10 @@ func (b *LesApiBackend) ExtRPCEnabled() bool { return b.extRPCEnabled } +func (b *LesApiBackend) UnprotectedAllowed() bool { + return b.allowUnprotectedTxs +} + func (b *LesApiBackend) RPCGasCap() uint64 { return b.eth.config.RPCGasCap } diff --git a/les/client.go b/les/client.go index d08c9feba..faaf6095e 100644 --- a/les/client.go +++ b/les/client.go @@ -157,7 +157,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) } - leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), leth, nil} + leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, leth, nil} gpoParams := config.GPO if gpoParams.Default == nil { gpoParams.Default = config.Miner.GasPrice diff --git a/node/config.go b/node/config.go index 447a69505..ef1da15d7 100644 --- a/node/config.go +++ b/node/config.go @@ -191,6 +191,9 @@ type Config struct { staticNodesWarning bool trustedNodesWarning bool oldGethResourceWarning bool + + // AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC. + AllowUnprotectedTxs bool `toml:",omitempty"` } // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into