check block number and timestamp options when filtering conditional transactions

This commit is contained in:
jj1980a 2024-10-10 15:52:26 +04:00 committed by Jerry
parent bfede06618
commit a106bd18f7
3 changed files with 151 additions and 13 deletions

View file

@ -1673,7 +1673,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)
@ -1685,7 +1685,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)
@ -1701,7 +1701,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()

View file

@ -531,14 +531,26 @@ func (l *list) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactio
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
}

View file

@ -97,7 +97,7 @@ 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.
@ -105,6 +105,10 @@ func TestFilterTxConditional(t *testing.T) {
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()
@ -118,7 +122,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)
@ -151,7 +155,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)
@ -165,7 +169,129 @@ func TestFilterTxConditional(t *testing.T) {
fmt.Println("after2", trie.Hash())
// 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)