diff --git a/core/state_processor.go b/core/state_processor.go index c17685b6be..64ebd6fabe 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -121,7 +121,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*types.Receipt, error) { - if config.IsDonut(blockNumber) && !tx.Protected() { + if config.IsDonut(blockNumber) && !config.IsEspresso(blockNumber) && !tx.Protected() { return nil, ErrUnprotectedTransaction } diff --git a/core/tx_pool.go b/core/tx_pool.go index 67b6ce3ea2..0752f2f36d 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -506,20 +506,6 @@ func (pool *TxPool) setGasLimit(gasLimit uint64) { } } -// handleDonutActivation removes from the pool all transactions without EIP-155 replay protection -func (pool *TxPool) handleDonutActivation() { - toRemove := make(map[common.Hash]struct{}) - pool.all.Range(func(hash common.Hash, tx *types.Transaction, _ bool) bool { - if !tx.Protected() { - toRemove[hash] = struct{}{} - } - return true - }, true, true) - for hash := range toRemove { - pool.removeTx(hash, true) - } -} - // Nonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. func (pool *TxPool) Nonce(addr common.Address) uint64 { @@ -651,7 +637,7 @@ func (pool *TxPool) ctx() *txPoolContext { // validateTx checks whether a transaction is valid according to the consensus // rules and adheres to some heuristic limits of the local node (price and size). func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { - if pool.donut && !tx.Protected() { + if pool.donut && !pool.espresso && !tx.Protected() { return ErrUnprotectedTransaction } if tx.EthCompatible() && !pool.donut { @@ -1391,12 +1377,8 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { // Update all fork indicator by next pending block number. next := new(big.Int).Add(newHead.Number, big.NewInt(1)) - wasDonut := pool.donut pool.istanbul = pool.chainconfig.IsIstanbul(next) pool.donut = pool.chainconfig.IsDonut(next) - if pool.donut && !wasDonut { - pool.handleDonutActivation() - } pool.espresso = pool.chainconfig.IsEspresso(next) } diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 5089ddb512..9ed1de9823 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -893,42 +893,72 @@ func TestTransactionGapFilling(t *testing.T) { // (a) to set pool.donut = false at its start (so we can add unprotected transactions) // (b) different functions to generate protected vs unprotected transactions, since we will // need to update transaction() and the others to use replay protection -func TestHandleDonutActivation(t *testing.T) { +func TestPoolReAcceptingUnprotectedTxsFromEFork(t *testing.T) { t.Parallel() - // Create a test account and fund it pool, key := setupTxPool() + // Create a test account and fund it defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) pool.currentState.AddBalance(account, big.NewInt(1000000)) + // flag it as before donut + pool.donut = false + pool.espresso = false + pool.AddRemotesSync([]*types.Transaction{ protectedTransaction(0, 100000, key), transaction(1, 100000, key), - protectedTransaction(2, 100000, key), - transaction(7, 100000, key), - protectedTransaction(8, 100000, key), - transaction(9, 100000, key), - transaction(10, 100000, key), + + protectedTransaction(10, 100000, key), + transaction(11, 100000, key), }) pending, queued := pool.Stats() + if pending != 2 { + t.Fatalf("before donut, pending transactions mismatched: have %d, want %d", pending, 2) + } + if queued != 2 { + t.Fatalf("before donut, queued transactions mismatched: have %d, want %d", queued, 2) + } + + // In donut fork + pool.donut = true + + pool.AddRemotesSync([]*types.Transaction{ + protectedTransaction(2, 100000, key), + transaction(3, 100000, key), + + protectedTransaction(12, 100000, key), + transaction(13, 100000, key), + }) + + pending, queued = pool.Stats() if pending != 3 { - t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) + t.Fatalf("after donut, pending transactions mismatched: have %d, want %d", pending, 3) } - if queued != 4 { - t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 4) + if queued != 3 { + t.Fatalf("after donut, queued transactions mismatched: have %d, want %d", queued, 3) } - pool.handleDonutActivation() + // In E fork + // flag it as E hard fork + pool.espresso = true + pool.AddRemotesSync([]*types.Transaction{ + transaction(3, 100000, key), + protectedTransaction(4, 100000, key), + + transaction(13, 100000, key), + protectedTransaction(14, 100000, key), + }) pending, queued = pool.Stats() - if pending != 1 { - t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1) + if pending != 5 { + t.Fatalf("after espresso, pending transactions mismatched: have %d, want %d", pending, 5) } - if queued != 2 { - t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) + if queued != 5 { + t.Fatalf("after espresso, queued transactions mismatched: have %d, want %d", queued, 5) } if err := validateTxPoolInternals(pool); err != nil { diff --git a/eth/api_backend.go b/eth/api_backend.go index 7ee1f400f3..90916dda16 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -43,8 +43,9 @@ import ( // EthAPIBackend implements ethapi.Backend for full nodes type EthAPIBackend struct { - extRPCEnabled bool - eth *Ethereum + extRPCEnabled bool + allowUnprotectedTxs bool + eth *Ethereum } // ChainConfig returns the active chain configuration. @@ -360,6 +361,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 d575a80f5f..4104e9d4ca 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -244,7 +244,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, chainDb) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) - eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), eth} + eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), true, eth} // Setup DNS discovery iterators. dnsclient := dnsdisc.NewClient(dnsdisc.Config{}) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index ea336760f6..70b2bb27a5 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1623,15 +1623,21 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c if err := checkFeeFromCeloTx(ctx, b, tx); err != nil { return common.Hash{}, err } + currentBlockNumber := b.CurrentBlock().Number() if !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 !b.UnprotectedAllowed() { + // 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 for this node") + } + if b.ChainConfig().IsDonut(currentBlockNumber) && !b.ChainConfig().IsEspresso(currentBlockNumber) { + 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 } // Print a log with full tx details for manual investigations and interventions - signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) + signer := types.MakeSigner(b.ChainConfig(), currentBlockNumber) from, err := types.Sender(signer, tx) if err != nil { return common.Hash{}, err diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 36e9dfe6db..20b58fb88e 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -48,8 +48,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 34f78f0bff..74f646def9 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -334,6 +334,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 02d15d9053..81956adeed 100644 --- a/les/client.go +++ b/les/client.go @@ -187,7 +187,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) } - leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, leth} + leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), true, leth} leth.chainreader = &LightChainReader{ config: leth.chainConfig, diff --git a/light/txpool.go b/light/txpool.go index a8333520e8..9881cc4e67 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -362,7 +362,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error err error ) - if pool.donut && !tx.Protected() { + if pool.donut && !pool.espresso && !tx.Protected() { return core.ErrUnprotectedTransaction } if tx.EthCompatible() && !pool.donut { diff --git a/node/config.go b/node/config.go index a1b02a9489..c03ec969f4 100644 --- a/node/config.go +++ b/node/config.go @@ -198,8 +198,8 @@ type Config struct { trustedNodesWarning bool oldGethResourceWarning bool - // AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC. - AllowUnprotectedTxs bool `toml:",omitempty"` + // // 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