Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIP-50] optional replay protection #1748

Merged
merged 5 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.IsEHardfork(blockNumber) && !tx.Protected() {
return nil, ErrUnprotectedTransaction
}

Expand Down
20 changes: 1 addition & 19 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,20 +504,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 {
Expand Down Expand Up @@ -649,7 +635,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 {
Expand Down Expand Up @@ -1381,12 +1367,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)
}

Expand Down
60 changes: 45 additions & 15 deletions core/tx_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,42 +876,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.eHardfork = 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.eHardfork = 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 expresso, 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 expresso, queued transactions mismatched: have %d, want %d", queued, 5)
}

if err := validateTxPoolInternals(pool); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion light/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down