From 1e5d420fbc9f3c1acbaad04514dc6fc7ad287fe8 Mon Sep 17 00:00:00 2001 From: Edgar Aroutiounian Date: Wed, 4 Mar 2020 17:44:07 -0800 Subject: [PATCH] [Core + API + Node] Add raw tx size limitation (#2382) * Define constants for tx-data and check size limit in tx-pool * Check for raw data size limitation at RPC layer * Check for raw data size limitation at Network layer --- core/tx_pool.go | 6 +++--- core/types/tx_pool.go | 7 +++++++ internal/hmyapi/apiv1/transactionpool.go | 9 +++++++++ internal/hmyapi/apiv2/transactionpool.go | 9 +++++++++ node/node_handler.go | 9 +++++++++ 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index bb551ae1c..a4b32160a 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -179,7 +179,7 @@ var DefaultTxPoolConfig = TxPoolConfig{ AccountQueue: 64, GlobalQueue: 1024, - Lifetime: 3 * time.Hour, + Lifetime: 30 * time.Minute, Blacklist: map[common.Address]struct{}{}, } @@ -657,8 +657,8 @@ func (pool *TxPool) validateTx(tx types.PoolTransaction, local bool) error { if tx.ShardID() != pool.chain.CurrentBlock().ShardID() { return errors.WithMessagef(ErrInvalidShard, "transaction shard is %d", tx.ShardID()) } - // Heuristic limit, reject transactions over 32KB to prevent DOS attacks - if tx.Size() > 32*1024 { + // For DOS prevention, reject excessively large transactions. + if tx.Size() >= types.MaxPoolTransactionDataSize { return errors.WithMessagef(ErrOversizedData, "transaction size is %s", tx.Size().String()) } // Transactions can't be negative. This may never happen using RLP decoded diff --git a/core/types/tx_pool.go b/core/types/tx_pool.go index e32512934..6884518a2 100644 --- a/core/types/tx_pool.go +++ b/core/types/tx_pool.go @@ -11,6 +11,13 @@ import ( staking "github.com/harmony-one/harmony/staking/types" ) +const ( + //MaxPoolTransactionDataSize is a 32KB heuristic data limit for DOS prevention + MaxPoolTransactionDataSize = 32 * 1024 + //MaxEncodedPoolTransactionSize is a heuristic raw/encoded data size limit. It has an additional 10KB for metadata + MaxEncodedPoolTransactionSize = MaxPoolTransactionDataSize + (10 * 1024) +) + var ( // ErrUnknownPoolTxType is returned when attempting to assert a PoolTransaction to its concrete type ErrUnknownPoolTxType = errors.New("unknown transaction type in tx-pool") diff --git a/internal/hmyapi/apiv1/transactionpool.go b/internal/hmyapi/apiv1/transactionpool.go index 09d5f044a..342ca1d65 100644 --- a/internal/hmyapi/apiv1/transactionpool.go +++ b/internal/hmyapi/apiv1/transactionpool.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/harmony-one/harmony/accounts" + "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/rawdb" "github.com/harmony-one/harmony/core/types" internal_common "github.com/harmony-one/harmony/internal/common" @@ -209,6 +210,10 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen func (s *PublicTransactionPoolAPI) SendRawStakingTransaction( ctx context.Context, encodedTx hexutil.Bytes, ) (common.Hash, error) { + if len(encodedTx) >= types.MaxEncodedPoolTransactionSize { + err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx)) + return common.Hash{}, err + } tx := new(staking.StakingTransaction) if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err @@ -224,6 +229,10 @@ func (s *PublicTransactionPoolAPI) SendRawStakingTransaction( // SendRawTransaction will add the signed transaction to the transaction pool. // The sender is responsible for signing the transaction and using the correct nonce. func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) { + if len(encodedTx) >= types.MaxEncodedPoolTransactionSize { + err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx)) + return common.Hash{}, err + } tx := new(types.Transaction) if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err diff --git a/internal/hmyapi/apiv2/transactionpool.go b/internal/hmyapi/apiv2/transactionpool.go index 60c2f7e4b..3e6e1ab46 100644 --- a/internal/hmyapi/apiv2/transactionpool.go +++ b/internal/hmyapi/apiv2/transactionpool.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/harmony-one/harmony/accounts" + "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/rawdb" "github.com/harmony-one/harmony/core/types" internal_common "github.com/harmony-one/harmony/internal/common" @@ -207,6 +208,10 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen func (s *PublicTransactionPoolAPI) SendRawStakingTransaction( ctx context.Context, encodedTx hexutil.Bytes, ) (common.Hash, error) { + if len(encodedTx) >= types.MaxEncodedPoolTransactionSize { + err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx)) + return common.Hash{}, err + } tx := new(staking.StakingTransaction) if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err @@ -222,6 +227,10 @@ func (s *PublicTransactionPoolAPI) SendRawStakingTransaction( // SendRawTransaction will add the signed transaction to the transaction pool. // The sender is responsible for signing the transaction and using the correct nonce. func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) { + if len(encodedTx) >= types.MaxEncodedPoolTransactionSize { + err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx)) + return common.Hash{}, err + } tx := new(types.Transaction) if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err diff --git a/node/node_handler.go b/node/node_handler.go index 0bb6b119c..9ac98ff03 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -13,6 +13,7 @@ import ( proto_discovery "github.com/harmony-one/harmony/api/proto/discovery" proto_node "github.com/harmony-one/harmony/api/proto/node" "github.com/harmony-one/harmony/block" + "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/harmony-one/harmony/internal/ctxerror" @@ -187,6 +188,10 @@ func (node *Node) HandleMessage(content []byte, sender libp2p_peer.ID) { } func (node *Node) transactionMessageHandler(msgPayload []byte) { + if len(msgPayload) >= types.MaxEncodedPoolTransactionSize { + utils.Logger().Warn().Err(core.ErrOversizedData).Msgf("encoded tx size: %d", len(msgPayload)) + return + } if len(msgPayload) < 1 { utils.Logger().Debug().Msgf("Invalid transaction message size") return @@ -208,6 +213,10 @@ func (node *Node) transactionMessageHandler(msgPayload []byte) { } func (node *Node) stakingMessageHandler(msgPayload []byte) { + if len(msgPayload) >= types.MaxEncodedPoolTransactionSize { + utils.Logger().Warn().Err(core.ErrOversizedData).Msgf("encoded tx size: %d", len(msgPayload)) + return + } if len(msgPayload) < 1 { utils.Logger().Debug().Msgf("Invalid staking transaction message size") return