tests for tx size limit (32 KB)

This commit is contained in:
Michael Riabzev 2019-10-28 18:44:42 +02:00
parent db79143a13
commit 4fcbb92354
2 changed files with 62 additions and 2 deletions

View file

@ -223,6 +223,7 @@ type TxPool struct {
currentState *state.StateDB // Current state in the blockchain head
pendingNonces *txNoncer // Pending state tracking virtual nonces
currentMaxGas uint64 // Current gas limit for transaction caps
maxTxSize uint64 // Maximal size of allowed transaction in bytes
locals *accountSet // Set of local transaction to exempt from eviction rules
journal *txJournal // Journal of local transaction to back up to disk
@ -270,6 +271,9 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
reorgDoneCh: make(chan chan struct{}),
reorgShutdownCh: make(chan struct{}),
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
maxTxSize: 32 * 1024,
}
pool.locals = newAccountSet(pool.signer)
for _, addr := range config.Locals {
@ -510,8 +514,8 @@ func (pool *TxPool) local() map[common.Address]types.Transactions {
// 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 {
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
if tx.Size() > 32*1024 {
// Reject transactions over defined size to prevent DOS attacks
if uint64(tx.Size()) > pool.maxTxSize {
return ErrOversizedData
}
// Transactions can't be negative. This may never happen using RLP decoded

View file

@ -77,6 +77,13 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec
return tx
}
func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, numDataBytes uint64) *types.Transaction {
data := make([]byte, numDataBytes)
rand.Read(data)
tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key)
return tx
}
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
@ -754,6 +761,55 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
}
}
// Test the limit on transaction size is enforced correctly.
// This test verifies every transaction having allowed size
// is added to the pool, and longer transactions are rejected.
func TestAllowedTxSize(t *testing.T) {
t.Parallel()
// Create a test account and fund it
pool, key := setupTxPool()
defer pool.Stop()
account, _ := deriveSender(transaction(0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1024*int64(pool.currentMaxGas)))
// Compute maximal data size for transactions (lower bound).
// It is assumed the fields in the transaction (except of the data) are:
// nonce : at most 32 bytes
// gasPrice : at most 32 bytes
// gasLimit : at most 32 bytes
// To address : 20 bytes
// value : at most 32 bytes
// signature : 65 bytes
// All those fields are summed up to at most 213 bytes.
txSizeWithoutData := uint64(213)
maxDataSize := pool.maxTxSize - txSizeWithoutData
// Increase block gas limit to infinity
pool.currentMaxGas = 2 << 60
// Try adding a transaction with maximal allowed size
if err := pool.validateTx(pricedDataTransaction(0, pool.currentMaxGas, big.NewInt(0), key, maxDataSize), true); err != nil {
t.Fatalf("failed to add transaction of size close to maximal: %d, %v", int(pricedDataTransaction(0, pool.currentMaxGas, big.NewInt(1), key, maxDataSize).Size()), err)
}
// Try adding a transaction with random allowed size
if err := pool.validateTx(pricedDataTransaction(0, pool.currentMaxGas, big.NewInt(0), key, uint64(rand.Intn(int(maxDataSize)))), true); err != nil {
t.Fatalf("failed to add transaction of random allowed size: %v", err)
}
// Try adding a transaction of minimal not allowed size
if pool.validateTx(pricedDataTransaction(0, pool.currentMaxGas, big.NewInt(0), key, pool.maxTxSize), true) == nil {
t.Fatalf("expected rejection on slightly oversize transaction")
}
// Try adding a transaction of random not allowed size
if pool.validateTx(pricedDataTransaction(0, pool.currentMaxGas, big.NewInt(1), key, maxDataSize+1+uint64(rand.Intn(int(10*pool.maxTxSize)))), true) == nil {
t.Fatalf("expected rejection on oversize transaction")
}
}
// Tests that if the transaction count belonging to multiple accounts go above
// some threshold, the higher transactions are dropped to prevent DOS attacks.
//