Skip to content

Commit 8830ed5

Browse files
authored
[CIP-50] optional replay protection (#1748)
* [cip-50] Optional replay protection * Fix espresso check * Fix espresso name in tests * Re add unprotected txs from rpc
1 parent 7ed4e1d commit 8830ed5

11 files changed

+75
-47
lines changed

core/state_processor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
121121
}
122122

123123
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) {
124-
if config.IsDonut(blockNumber) && !tx.Protected() {
124+
if config.IsDonut(blockNumber) && !config.IsEspresso(blockNumber) && !tx.Protected() {
125125
return nil, ErrUnprotectedTransaction
126126
}
127127

core/tx_pool.go

+1-19
Original file line numberDiff line numberDiff line change
@@ -506,20 +506,6 @@ func (pool *TxPool) setGasLimit(gasLimit uint64) {
506506
}
507507
}
508508

509-
// handleDonutActivation removes from the pool all transactions without EIP-155 replay protection
510-
func (pool *TxPool) handleDonutActivation() {
511-
toRemove := make(map[common.Hash]struct{})
512-
pool.all.Range(func(hash common.Hash, tx *types.Transaction, _ bool) bool {
513-
if !tx.Protected() {
514-
toRemove[hash] = struct{}{}
515-
}
516-
return true
517-
}, true, true)
518-
for hash := range toRemove {
519-
pool.removeTx(hash, true)
520-
}
521-
}
522-
523509
// Nonce returns the next nonce of an account, with all transactions executable
524510
// by the pool already applied on top.
525511
func (pool *TxPool) Nonce(addr common.Address) uint64 {
@@ -651,7 +637,7 @@ func (pool *TxPool) ctx() *txPoolContext {
651637
// validateTx checks whether a transaction is valid according to the consensus
652638
// rules and adheres to some heuristic limits of the local node (price and size).
653639
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
654-
if pool.donut && !tx.Protected() {
640+
if pool.donut && !pool.espresso && !tx.Protected() {
655641
return ErrUnprotectedTransaction
656642
}
657643
if tx.EthCompatible() && !pool.donut {
@@ -1391,12 +1377,8 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
13911377

13921378
// Update all fork indicator by next pending block number.
13931379
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
1394-
wasDonut := pool.donut
13951380
pool.istanbul = pool.chainconfig.IsIstanbul(next)
13961381
pool.donut = pool.chainconfig.IsDonut(next)
1397-
if pool.donut && !wasDonut {
1398-
pool.handleDonutActivation()
1399-
}
14001382
pool.espresso = pool.chainconfig.IsEspresso(next)
14011383
}
14021384

core/tx_pool_test.go

+45-15
Original file line numberDiff line numberDiff line change
@@ -893,42 +893,72 @@ func TestTransactionGapFilling(t *testing.T) {
893893
// (a) to set pool.donut = false at its start (so we can add unprotected transactions)
894894
// (b) different functions to generate protected vs unprotected transactions, since we will
895895
// need to update transaction() and the others to use replay protection
896-
func TestHandleDonutActivation(t *testing.T) {
896+
func TestPoolReAcceptingUnprotectedTxsFromEFork(t *testing.T) {
897897
t.Parallel()
898898

899-
// Create a test account and fund it
900899
pool, key := setupTxPool()
900+
// Create a test account and fund it
901901
defer pool.Stop()
902902

903903
account := crypto.PubkeyToAddress(key.PublicKey)
904904
pool.currentState.AddBalance(account, big.NewInt(1000000))
905905

906+
// flag it as before donut
907+
pool.donut = false
908+
pool.espresso = false
909+
906910
pool.AddRemotesSync([]*types.Transaction{
907911
protectedTransaction(0, 100000, key),
908912
transaction(1, 100000, key),
909-
protectedTransaction(2, 100000, key),
910-
transaction(7, 100000, key),
911-
protectedTransaction(8, 100000, key),
912-
transaction(9, 100000, key),
913-
transaction(10, 100000, key),
913+
914+
protectedTransaction(10, 100000, key),
915+
transaction(11, 100000, key),
914916
})
915917

916918
pending, queued := pool.Stats()
919+
if pending != 2 {
920+
t.Fatalf("before donut, pending transactions mismatched: have %d, want %d", pending, 2)
921+
}
922+
if queued != 2 {
923+
t.Fatalf("before donut, queued transactions mismatched: have %d, want %d", queued, 2)
924+
}
925+
926+
// In donut fork
927+
pool.donut = true
928+
929+
pool.AddRemotesSync([]*types.Transaction{
930+
protectedTransaction(2, 100000, key),
931+
transaction(3, 100000, key),
932+
933+
protectedTransaction(12, 100000, key),
934+
transaction(13, 100000, key),
935+
})
936+
937+
pending, queued = pool.Stats()
917938
if pending != 3 {
918-
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
939+
t.Fatalf("after donut, pending transactions mismatched: have %d, want %d", pending, 3)
919940
}
920-
if queued != 4 {
921-
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 4)
941+
if queued != 3 {
942+
t.Fatalf("after donut, queued transactions mismatched: have %d, want %d", queued, 3)
922943
}
923944

924-
pool.handleDonutActivation()
945+
// In E fork
946+
// flag it as E hard fork
947+
pool.espresso = true
948+
pool.AddRemotesSync([]*types.Transaction{
949+
transaction(3, 100000, key),
950+
protectedTransaction(4, 100000, key),
951+
952+
transaction(13, 100000, key),
953+
protectedTransaction(14, 100000, key),
954+
})
925955

926956
pending, queued = pool.Stats()
927-
if pending != 1 {
928-
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1)
957+
if pending != 5 {
958+
t.Fatalf("after espresso, pending transactions mismatched: have %d, want %d", pending, 5)
929959
}
930-
if queued != 2 {
931-
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
960+
if queued != 5 {
961+
t.Fatalf("after espresso, queued transactions mismatched: have %d, want %d", queued, 5)
932962
}
933963

934964
if err := validateTxPoolInternals(pool); err != nil {

eth/api_backend.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ import (
4343

4444
// EthAPIBackend implements ethapi.Backend for full nodes
4545
type EthAPIBackend struct {
46-
extRPCEnabled bool
47-
eth *Ethereum
46+
extRPCEnabled bool
47+
allowUnprotectedTxs bool
48+
eth *Ethereum
4849
}
4950

5051
// ChainConfig returns the active chain configuration.
@@ -360,6 +361,10 @@ func (b *EthAPIBackend) ExtRPCEnabled() bool {
360361
return b.extRPCEnabled
361362
}
362363

364+
func (b *EthAPIBackend) UnprotectedAllowed() bool {
365+
return b.allowUnprotectedTxs
366+
}
367+
363368
func (b *EthAPIBackend) RPCGasCap() uint64 {
364369
return b.eth.config.RPCGasCap
365370
}

eth/backend.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
244244
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, chainDb)
245245
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
246246

247-
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), eth}
247+
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), true, eth}
248248

249249
// Setup DNS discovery iterators.
250250
dnsclient := dnsdisc.NewClient(dnsdisc.Config{})

internal/ethapi/api.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -1623,15 +1623,21 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
16231623
if err := checkFeeFromCeloTx(ctx, b, tx); err != nil {
16241624
return common.Hash{}, err
16251625
}
1626+
currentBlockNumber := b.CurrentBlock().Number()
16261627
if !tx.Protected() {
1627-
// Ensure only eip155 signed transactions are submitted if EIP155Required is set.
1628-
return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC")
1628+
if !b.UnprotectedAllowed() {
1629+
// Ensure only eip155 signed transactions are submitted if EIP155Required is set.
1630+
return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC for this node")
1631+
}
1632+
if b.ChainConfig().IsDonut(currentBlockNumber) && !b.ChainConfig().IsEspresso(currentBlockNumber) {
1633+
return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC")
1634+
}
16291635
}
16301636
if err := b.SendTx(ctx, tx); err != nil {
16311637
return common.Hash{}, err
16321638
}
16331639
// Print a log with full tx details for manual investigations and interventions
1634-
signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
1640+
signer := types.MakeSigner(b.ChainConfig(), currentBlockNumber)
16351641
from, err := types.Sender(signer, tx)
16361642
if err != nil {
16371643
return common.Hash{}, err

internal/ethapi/backend.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ type Backend interface {
4848
ChainDb() ethdb.Database
4949
AccountManager() *accounts.Manager
5050
ExtRPCEnabled() bool
51-
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
52-
RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
51+
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
52+
RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
53+
UnprotectedAllowed() bool // allows only for EIP155 transactions.
5354

5455
// Blockchain API
5556
SetHead(number uint64)

les/api_backend.go

+4
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,10 @@ func (b *LesApiBackend) ExtRPCEnabled() bool {
334334
return b.extRPCEnabled
335335
}
336336

337+
func (b *LesApiBackend) UnprotectedAllowed() bool {
338+
return b.allowUnprotectedTxs
339+
}
340+
337341
func (b *LesApiBackend) RPCGasCap() uint64 {
338342
return b.eth.config.RPCGasCap
339343
}

les/client.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
187187
rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
188188
}
189189

190-
leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, leth}
190+
leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), true, leth}
191191

192192
leth.chainreader = &LightChainReader{
193193
config: leth.chainConfig,

light/txpool.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
362362
err error
363363
)
364364

365-
if pool.donut && !tx.Protected() {
365+
if pool.donut && !pool.espresso && !tx.Protected() {
366366
return core.ErrUnprotectedTransaction
367367
}
368368
if tx.EthCompatible() && !pool.donut {

node/config.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ type Config struct {
198198
trustedNodesWarning bool
199199
oldGethResourceWarning bool
200200

201-
// AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC.
202-
AllowUnprotectedTxs bool `toml:",omitempty"`
201+
// // AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC.
202+
// AllowUnprotectedTxs bool `toml:",omitempty"`
203203
}
204204

205205
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into

0 commit comments

Comments
 (0)