alow multiple slots transactions

This commit is contained in:
Michael Riabzev 2019-11-07 10:17:21 +02:00 committed by Péter Szilágyi
parent 315c239f06
commit de111ef4b7
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
3 changed files with 54 additions and 11 deletions

View file

@ -494,11 +494,11 @@ func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) boo
// Discard finds a number of most underpriced transactions, removes them from the
// priced list and returns them for further removal from the entire pool.
func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions {
drop := make(types.Transactions, 0, count) // Remote underpriced transactions to drop
func (l *txPricedList) Discard(numSlots int, local *accountSet) types.Transactions {
drop := make(types.Transactions, 0, numSlots) // Remote underpriced transactions to drop
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
for len(*l.items) > 0 && count > 0 {
for len(*l.items) > 0 && numSlots > 0 {
// Discard stale transactions if found during cleanup
tx := heap.Pop(l.items).(*types.Transaction)
if l.all.Get(tx.Hash()) == nil {
@ -510,7 +510,7 @@ func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions
save = append(save, tx)
} else {
drop = append(drop, tx)
count--
numSlots -= NumSlots(tx)
}
}
for _, tx := range save {

View file

@ -584,7 +584,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
return false, ErrUnderpriced
}
// New transaction is better than our worse ones, make room for it
drop := pool.priced.Discard(pool.all.Count()-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1), pool.locals)
drop := pool.priced.Discard(pool.all.SlotsUsed()-int(pool.config.GlobalSlots+pool.config.GlobalQueue)+NumSlots(tx), pool.locals)
for _, tx := range drop {
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
underpricedTxMeter.Mark(1)
@ -1143,8 +1143,8 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
pool.pendingNonces = newTxNoncer(statedb)
pool.currentMaxGas = newHead.GasLimit
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
pool.maxTxSize = 32 * 1024
// Heuristic limit, reject transactions over 4 slots to prevent DOS attacks
pool.maxTxSize = 4 * slotSize
// Inject any transactions discarded due to reorgs
log.Debug("Reinjecting stale transactions", "count", len(reinject))
@ -1498,6 +1498,7 @@ func (as *accountSet) merge(other *accountSet) {
// TxPool.mu mutex.
type txLookup struct {
all map[common.Hash]*types.Transaction
numUsedSlots int
lock sync.RWMutex
}
@ -1505,6 +1506,7 @@ type txLookup struct {
func newTxLookup() *txLookup {
return &txLookup{
all: make(map[common.Hash]*types.Transaction),
numUsedSlots: 0,
}
}
@ -1536,11 +1538,32 @@ func (t *txLookup) Count() int {
return len(t.all)
}
// SlotsUsed returns the current number of slots used in the lookup.
func (t *txLookup) SlotsUsed() int {
t.lock.RLock()
defer t.lock.RUnlock()
return t.numUsedSlots
}
const (
// Transactions are kept in slots.
// Each slot is defined to be 32KB.
// This slots mechanism provides protection against
// resources over consumption.
slotSize = 32 * 1024
)
func NumSlots(tx *types.Transaction) int {
return int(math.Ceil(float64(tx.Size()) / slotSize))
}
// Add adds a transaction to the lookup.
func (t *txLookup) Add(tx *types.Transaction) {
t.lock.Lock()
defer t.lock.Unlock()
t.numUsedSlots += NumSlots(tx)
t.all[tx.Hash()] = tx
}
@ -1549,5 +1572,6 @@ func (t *txLookup) Remove(hash common.Hash) {
t.lock.Lock()
defer t.lock.Unlock()
t.numUsedSlots -= NumSlots(t.all[hash])
delete(t.all, hash)
}

View file

@ -1808,6 +1808,25 @@ func TestTransactionStatusCheck(t *testing.T) {
}
}
// Test the transaction slots consumption is computed correctly
func TestNumSlots(t *testing.T) {
t.Parallel()
key, _ := crypto.GenerateKey()
tinyTx := pricedDataTransaction(0, 0, big.NewInt(0), key, 0)
if NumSlots(tinyTx) != 1 {
t.Fatalf("Small transactions are expected to consume a single slot.")
}
numAdditionalSlots := rand.Intn(10)
dataLen := uint64(slotSize * numAdditionalSlots)
severalSlotsTx := pricedDataTransaction(0, 0, big.NewInt(0), key, dataLen)
if actual := NumSlots(severalSlotsTx); actual != 1+numAdditionalSlots {
t.Fatalf("Unexpected slots consumptions: expected %d, actual %d", 1+numAdditionalSlots, actual)
}
}
// Benchmarks the speed of validating the contents of the pending queue of the
// transaction pool.
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }