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

Check block number and timestamp options when filtering conditional transactions #1350

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 3 additions & 3 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1655,7 +1655,7 @@ func (pool *LegacyPool) truncateQueue() {
// to trigger a re-heap is this function
func (pool *LegacyPool) demoteUnexecutables() {
// Iterate over all accounts and demote any non-executable transactions
gasLimit := pool.currentHead.Load().GasLimit
currentHeader := pool.currentHead.Load()
for addr, list := range pool.pending {
nonce := pool.currentState.GetNonce(addr)

Expand All @@ -1667,7 +1667,7 @@ func (pool *LegacyPool) demoteUnexecutables() {
log.Trace("Removed old pending transaction", "hash", hash)
}
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
drops, invalids := list.Filter(pool.currentState.GetBalance(addr), gasLimit)
drops, invalids := list.Filter(pool.currentState.GetBalance(addr), currentHeader.GasLimit)
for _, tx := range drops {
hash := tx.Hash()
log.Trace("Removed unpayable pending transaction", "hash", hash)
Expand All @@ -1683,7 +1683,7 @@ func (pool *LegacyPool) demoteUnexecutables() {
pool.enqueueTx(hash, tx, false, false)
}
// Drop all transactions that no longer have valid TxOptions
txConditionalsRemoved := list.FilterTxConditional(pool.currentState)
txConditionalsRemoved := list.FilterTxConditional(pool.currentState, currentHeader)

for _, tx := range txConditionalsRemoved {
hash := tx.Hash()
Expand Down
24 changes: 18 additions & 6 deletions core/txpool/legacypool/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,14 +523,26 @@ func (l *list) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions,
return removed, invalids
}

// FilterTxConditional returns the conditional transactions with invalid KnownAccounts
// TODO - We will also have to check block range and time stamp range!
func (l *list) FilterTxConditional(state *state.StateDB) types.Transactions {
// FilterTxConditional returns the conditional transactions with invalid PIP15 options
func (l *list) FilterTxConditional(state *state.StateDB, header *types.Header) types.Transactions {
if state == nil || header == nil {
return nil
}

removed := l.txs.filter(func(tx *types.Transaction) bool {
if options := tx.GetOptions(); options != nil {
err := state.ValidateKnownAccounts(options.KnownAccounts)
if err != nil {
log.Error("Error while Filtering Tx Conditional", "err", err)
if err := state.ValidateKnownAccounts(options.KnownAccounts); err != nil {
log.Debug("Error while Filtering Tx Conditional's known accounts", "err", err)
return true
}

if err := header.ValidateBlockNumberOptionsPIP15(options.BlockNumberMin, options.BlockNumberMax); err != nil {
log.Debug("Error while Filtering Tx Conditional's block number options", "err", err)
return true
}

if err := header.ValidateTimestampOptionsPIP15(options.TimestampMin, options.TimestampMax); err != nil {
log.Debug("Error while Filtering Tx Conditional's timestamp options", "err", err)
return true
}

Expand Down
134 changes: 130 additions & 4 deletions core/txpool/legacypool/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,18 @@ func BenchmarkListAdd(b *testing.B) {
}
}

func TestFilterTxConditional(t *testing.T) {
func TestFilterTxConditionalKnownAccounts(t *testing.T) {
t.Parallel()

// Create an in memory state db to test against.
memDb := rawdb.NewMemoryDatabase()
db := state.NewDatabase(memDb)
state, _ := state.New(common.Hash{}, db, nil)

header := &types.Header{
Number: big.NewInt(0),
}

// Create a private key to sign transactions.
key, _ := crypto.GenerateKey()

Expand All @@ -100,7 +104,7 @@ func TestFilterTxConditional(t *testing.T) {

// There should be no drops at this point.
// No state has been modified.
drops := list.FilterTxConditional(state)
drops := list.FilterTxConditional(state, header)

count := len(drops)
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)
Expand All @@ -122,7 +126,7 @@ func TestFilterTxConditional(t *testing.T) {
list.Add(tx2, DefaultConfig.PriceBump)

// There should still be no drops as no state has been modified.
drops = list.FilterTxConditional(state)
drops = list.FilterTxConditional(state, header)

count = len(drops)
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)
Expand All @@ -131,7 +135,129 @@ func TestFilterTxConditional(t *testing.T) {
state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{31: 1})

// tx2 should be the single transaction filtered out
drops = list.FilterTxConditional(state)
drops = list.FilterTxConditional(state, header)

count = len(drops)
require.Equal(t, 1, count, "got %d filtered by TxOptions when there should be a single one", count)

require.Equal(t, tx2, drops[0], "Got %x, expected %x", drops[0].Hash(), tx2.Hash())
}

func TestFilterTxConditionalBlockNumber(t *testing.T) {
t.Parallel()

// Create an in memory state db to test against.
memDb := rawdb.NewMemoryDatabase()
db := state.NewDatabase(memDb)
state, _ := state.New(common.Hash{}, db, nil)

header := &types.Header{
Number: big.NewInt(100),
}

// Create a private key to sign transactions.
key, _ := crypto.GenerateKey()

// Create a list.
list := newList(true)

// Create a transaction with no defined tx options
// and add to the list.
tx := transaction(0, 1000, key)
list.Add(tx, DefaultConfig.PriceBump)

// There should be no drops at this point.
// No state has been modified.
drops := list.FilterTxConditional(state, header)

count := len(drops)
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)

// Create another transaction with a block number option and add to the list.
tx2 := transaction(1, 1000, key)

var options types.OptionsPIP15

options.BlockNumberMin = big.NewInt(90)
options.BlockNumberMax = big.NewInt(110)

tx2.PutOptions(&options)
list.Add(tx2, DefaultConfig.PriceBump)

// There should still be no drops as no state has been modified.
drops = list.FilterTxConditional(state, header)

count = len(drops)
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)

// Set block number that conflicts with tx2's policy
header.Number = big.NewInt(120)

// tx2 should be the single transaction filtered out
drops = list.FilterTxConditional(state, header)

count = len(drops)
require.Equal(t, 1, count, "got %d filtered by TxOptions when there should be a single one", count)

require.Equal(t, tx2, drops[0], "Got %x, expected %x", drops[0].Hash(), tx2.Hash())
}

func TestFilterTxConditionalTimestamp(t *testing.T) {
t.Parallel()

// Create an in memory state db to test against.
memDb := rawdb.NewMemoryDatabase()
db := state.NewDatabase(memDb)
state, _ := state.New(common.Hash{}, db, nil)

header := &types.Header{
Number: big.NewInt(0),
Time: 100,
}

// Create a private key to sign transactions.
key, _ := crypto.GenerateKey()

// Create a list.
list := newList(true)

// Create a transaction with no defined tx options
// and add to the list.
tx := transaction(0, 1000, key)
list.Add(tx, DefaultConfig.PriceBump)

// There should be no drops at this point.
// No state has been modified.
drops := list.FilterTxConditional(state, header)

count := len(drops)
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)

// Create another transaction with a timestamp option and add to the list.
tx2 := transaction(1, 1000, key)

var options types.OptionsPIP15

minTimestamp := uint64(90)
maxTimestamp := uint64(110)

options.TimestampMin = &minTimestamp
options.TimestampMax = &maxTimestamp

tx2.PutOptions(&options)
list.Add(tx2, DefaultConfig.PriceBump)

// There should still be no drops as no state has been modified.
drops = list.FilterTxConditional(state, header)

count = len(drops)
require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count)

// Set timestamp that conflicts with tx2's policy
header.Time = 120

// tx2 should be the single transaction filtered out
drops = list.FilterTxConditional(state, header)

count = len(drops)
require.Equal(t, 1, count, "got %d filtered by TxOptions when there should be a single one", count)
Expand Down
Loading