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

a few spam protections #3769

Merged
merged 12 commits into from
Jun 15, 2021
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
9 changes: 6 additions & 3 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ type TxPoolConfig struct {
Rejournal time.Duration // Time interval to regenerate the local transaction journal

PriceLimit uint64 // Minimum gas price to enforce for acceptance into the pool
PriceBump uint64 // Minimum price bump percentage to replace an already existing transaction (nonce)
PriceBump uint64 // Minimum price bump to replace an already existing transaction (nonce)

AccountSlots uint64 // Number of executable transaction slots guaranteed per account
GlobalSlots uint64 // Maximum number of executable transaction slots for all accounts
Expand All @@ -170,7 +170,7 @@ var DefaultTxPoolConfig = TxPoolConfig{
Journal: "transactions.rlp",
Rejournal: time.Hour,

PriceLimit: 1,
PriceLimit: 1e9, // 1 Gwei/Nano
PriceBump: 10,

AccountSlots: 16,
Expand Down Expand Up @@ -720,7 +720,10 @@ func (pool *TxPool) validateTx(tx types.PoolTransaction, local bool) error {
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
gasPrice := new(big.Float).SetInt64(tx.GasPrice().Int64())
gasPrice = gasPrice.Mul(gasPrice, new(big.Float).SetFloat64(1e-9)) // Gas-price is in Nano
return errors.WithMessagef(ErrUnderpriced, "transaction gas-price is %.18f ONE", gasPrice)

minGasPrice := new(big.Float).SetInt64(pool.gasPrice.Int64())
minGasPrice = minGasPrice.Mul(minGasPrice, new(big.Float).SetFloat64(1e-9)) // Gas-price is in Nano
return errors.WithMessagef(ErrUnderpriced, "transaction gas-price is %.18f ONE; minimum gas price is %.18f ONE", gasPrice, minGasPrice)
}
// Ensure the transaction adheres to nonce ordering
if pool.currentState.GetNonce(from) > tx.Nonce() {
Expand Down
108 changes: 56 additions & 52 deletions core/tx_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,17 @@ func stakingCreateValidatorTransaction(key *ecdsa.PrivateKey) (*staking.StakingT
}
}

gasPrice := big.NewInt(10000)
gasPrice := big.NewInt(1000000000)
tx, _ := staking.NewStakingTransaction(0, 1e10, gasPrice, stakePayloadMaker)
return staking.Sign(tx, staking.NewEIP155Signer(tx.ChainID()), key)
}

func transaction(shardID uint32, nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) types.PoolTransaction {
return pricedTransaction(shardID, nonce, gaslimit, big.NewInt(1), key)
return pricedTransaction(shardID, nonce, gaslimit, big.NewInt(1000000000), key)
}

func pricedTransaction(shardID uint32, nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) types.PoolTransaction {
signedTx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, shardID, big.NewInt(100), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
signedTx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, shardID, big.NewInt(100000000000), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
return signedTx
}

Expand Down Expand Up @@ -307,7 +307,7 @@ func TestInvalidTransactions(t *testing.T) {
}

tx = transaction(0, 1, 100000, key)
pool.gasPrice = big.NewInt(1000)
pool.gasPrice = big.NewInt(1000000000000)
if err := pool.AddRemote(tx); err != ErrUnderpriced {
t.Error("expected", ErrUnderpriced, "got", err)
}
Expand Down Expand Up @@ -406,7 +406,7 @@ func TestCreateValidatorTransaction(t *testing.T) {
t.Errorf("cannot create new staking transaction, %v\n", err)
}
senderAddr, _ := stx.SenderAddress()
pool.currentState.AddBalance(senderAddr, tenKOnes)
pool.currentState.AddBalance(senderAddr, hundredKOnes)
// Add additional create validator tx cost
pool.currentState.AddBalance(senderAddr, cost)

Expand All @@ -432,14 +432,14 @@ func TestMixedTransactions(t *testing.T) {
t.Errorf("cannot create new staking transaction, %v\n", err)
}
stxAddr, _ := stx.SenderAddress()
pool.currentState.AddBalance(stxAddr, tenKOnes)
pool.currentState.AddBalance(stxAddr, hundredKOnes)
// Add additional create validator tx cost
pool.currentState.AddBalance(stxAddr, cost)

goodFromKey, _ := crypto.GenerateKey()
tx := transaction(0, 0, 25000, goodFromKey)
txAddr, _ := deriveSender(tx)
pool.currentState.AddBalance(txAddr, big.NewInt(50100))
pool.currentState.AddBalance(txAddr, big.NewInt(50100000000000))

errs := pool.AddRemotes(types.PoolTransactions{stx, tx})
for _, err := range errs {
Expand Down Expand Up @@ -472,8 +472,8 @@ func TestBlacklistedTransactions(t *testing.T) {
goodFromAcc, _ := deriveSender(goodTx)

// Fund from accounts
pool.currentState.AddBalance(bannedFromAcc, big.NewInt(50100))
pool.currentState.AddBalance(goodFromAcc, big.NewInt(50100))
pool.currentState.AddBalance(bannedFromAcc, big.NewInt(50100000000000))
pool.currentState.AddBalance(goodFromAcc, big.NewInt(50100000000000))

DefaultTxPoolConfig.Blacklist[bannedToAcc] = struct{}{}
err := pool.AddRemotes(types.PoolTransactions{badTx})
Expand Down Expand Up @@ -506,7 +506,7 @@ func TestTransactionQueue(t *testing.T) {

tx := transaction(0, 0, 100, key)
from, _ := deriveSender(tx)
pool.currentState.AddBalance(from, big.NewInt(1000))
pool.currentState.AddBalance(from, big.NewInt(1000000000000))
pool.lockedReset(nil, nil)
pool.enqueueTx(tx)

Expand Down Expand Up @@ -535,7 +535,7 @@ func TestTransactionQueue(t *testing.T) {
tx2 := transaction(0, 10, 100, key)
tx3 := transaction(0, 11, 100, key)
from, _ = deriveSender(tx1)
pool.currentState.AddBalance(from, big.NewInt(1000))
pool.currentState.AddBalance(from, big.NewInt(1000000000000))
pool.lockedReset(nil, nil)

pool.enqueueTx(tx1)
Expand Down Expand Up @@ -577,7 +577,7 @@ func TestTransactionChainFork(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
statedb.AddBalance(addr, big.NewInt(100000000000000))
statedb.AddBalance(addr, big.NewInt(9000000000000000))

pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
pool.lockedReset(nil, nil)
Expand Down Expand Up @@ -606,7 +606,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
statedb.AddBalance(addr, big.NewInt(100000000000000))
statedb.AddBalance(addr, big.NewInt(100000000000000000))

pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
pool.lockedReset(nil, nil)
Expand All @@ -615,13 +615,13 @@ func TestTransactionDoubleNonce(t *testing.T) {

signer := types.HomesteadSigner{}
tx1, _ := types.SignTx(
types.NewTransaction(0, common.Address{}, 0, big.NewInt(100), 100000, big.NewInt(1), nil),
types.NewTransaction(0, common.Address{}, 0, big.NewInt(100), 100000, big.NewInt(1000000000), nil),
signer, key)
tx2, _ := types.SignTx(
types.NewTransaction(0, common.Address{}, 0, big.NewInt(100), 1000000, big.NewInt(2), nil),
types.NewTransaction(0, common.Address{}, 0, big.NewInt(100), 1000000, big.NewInt(2000000000), nil),
signer, key)
tx3, _ := types.SignTx(
types.NewTransaction(0, common.Address{}, 0, big.NewInt(100), 1000000, big.NewInt(1), nil),
types.NewTransaction(0, common.Address{}, 0, big.NewInt(100), 1000000, big.NewInt(1000000000), nil),
signer, key)

// Add the first two transaction, ensure higher priced stays only
Expand Down Expand Up @@ -660,7 +660,7 @@ func TestTransactionMissingNonce(t *testing.T) {
defer pool.Stop()

addr := crypto.PubkeyToAddress(key.PublicKey)
pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
pool.currentState.AddBalance(addr, big.NewInt(100100000000000))
tx := transaction(0, 1, 100000, key)
if _, err := pool.add(tx, false); err != nil {
t.Error("didn't expect error", err)
Expand All @@ -685,7 +685,7 @@ func TestTransactionNonceRecovery(t *testing.T) {

addr := crypto.PubkeyToAddress(key.PublicKey)
pool.currentState.SetNonce(addr, n)
pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
pool.currentState.AddBalance(addr, big.NewInt(100100000000000))
pool.lockedReset(nil, nil)

tx := transaction(0, n, 100000, key)
Expand All @@ -710,7 +710,7 @@ func TestTransactionDropping(t *testing.T) {
defer pool.Stop()

account, _ := deriveSender(transaction(0, 0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000))
pool.currentState.AddBalance(account, big.NewInt(1000000000000))

// Add some pending and some queued transactions
var (
Expand Down Expand Up @@ -749,7 +749,7 @@ func TestTransactionDropping(t *testing.T) {
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6)
}
// Reduce the balance of the account, and check that invalidated transactions are dropped
pool.currentState.AddBalance(account, big.NewInt(-650))
pool.currentState.AddBalance(account, big.NewInt(-650000000000))
pool.lockedReset(nil, nil)

if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
Expand Down Expand Up @@ -815,7 +815,7 @@ func TestTransactionPostponing(t *testing.T) {
keys[i], _ = crypto.GenerateKey()
accs[i] = crypto.PubkeyToAddress(keys[i].PublicKey)

pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100))
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100000000000))
}
// Add a batch consecutive pending transactions for validation
txs := types.PoolTransactions{}
Expand Down Expand Up @@ -867,8 +867,10 @@ func TestTransactionPostponing(t *testing.T) {
if _, ok := pool.pending[accs[0]].txs.items[txs[0].Nonce()]; !ok {
t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txs[0])
}
if _, ok := pool.queue[accs[0]].txs.items[txs[0].Nonce()]; ok {
t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txs[0])
if pool.queue[accs[0]] != nil {
if _, ok := pool.queue[accs[0]].txs.items[txs[0].Nonce()]; ok {
t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txs[0])
}
}
for i, tx := range txs[1:100] {
if i%2 == 1 {
Expand All @@ -882,8 +884,10 @@ func TestTransactionPostponing(t *testing.T) {
if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx)
}
if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx)
if pool.queue[accs[0]] != nil {
if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx)
}
}
}
}
Expand Down Expand Up @@ -918,7 +922,7 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
defer pool.Stop()

account, _ := deriveSender(transaction(0, 0, 0, key))
pool.currentState.AddBalance(account, big.NewInt(1000000))
pool.currentState.AddBalance(account, big.NewInt(900000000000000))

// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(1); i <= testTxPoolConfig.AccountQueue+5; i++ {
Expand Down Expand Up @@ -973,7 +977,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000000000000))
}
local := keys[len(keys)-1]

Expand Down Expand Up @@ -1063,14 +1067,14 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
local, _ := crypto.GenerateKey()
remote, _ := crypto.GenerateKey()

pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(900000000000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(900000000000000))

// Add the two transactions and ensure they both are queued up
if err := pool.AddLocal(pricedTransaction(0, 1, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(0, 1, 100000, big.NewInt(1000000000), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(0, 1, 100000, big.NewInt(1), remote)); err != nil {
if err := pool.AddRemote(pricedTransaction(0, 1, 100000, big.NewInt(1000000000), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
pending, queued := pool.Stats()
Expand Down Expand Up @@ -1119,7 +1123,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
defer pool1.Stop()

account1, _ := deriveSender(transaction(0, 0, 0, key1))
pool1.currentState.AddBalance(account1, big.NewInt(1000000))
pool1.currentState.AddBalance(account1, big.NewInt(9000000000000000))

for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
if err := pool1.AddRemote(transaction(0, origin+i, 100000, key1)); err != nil {
Expand All @@ -1131,7 +1135,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
defer pool2.Stop()

account2, _ := deriveSender(transaction(0, 0, 0, key2))
pool2.currentState.AddBalance(account2, big.NewInt(1000000))
pool2.currentState.AddBalance(account2, big.NewInt(9000000000000000))

txs := types.PoolTransactions{}
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
Expand Down Expand Up @@ -1299,17 +1303,17 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 3)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000*1000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000*1000000*1000000000))
}
// Create transaction (both pending and queued) with a linearly growing gasprice
for i := uint64(0); i < 500; i++ {
// Add pending
pTx := pricedTransaction(0, i, 100000, big.NewInt(int64(i)), keys[2])
pTx := pricedTransaction(0, i, 100000, big.NewInt(int64(i*1000000000)), keys[2])
if err := pool.AddLocal(pTx); err != nil {
t.Fatal(err)
}
// Add queued
qTx := pricedTransaction(0, i+501, 100000, big.NewInt(int64(i)), keys[2])
qTx := pricedTransaction(0, i+501, 100000, big.NewInt(int64(i*1000000000)), keys[2])
if err := pool.AddLocal(qTx); err != nil {
t.Fatal(err)
}
Expand All @@ -1332,13 +1336,13 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
validate()

// Reprice the pool and check that nothing is dropped
pool.SetGasPrice(big.NewInt(2))
pool.SetGasPrice(big.NewInt(2000000000))
validate()

pool.SetGasPrice(big.NewInt(2))
pool.SetGasPrice(big.NewInt(4))
pool.SetGasPrice(big.NewInt(8))
pool.SetGasPrice(big.NewInt(100))
pool.SetGasPrice(big.NewInt(2000000000))
pool.SetGasPrice(big.NewInt(4000000000))
pool.SetGasPrice(big.NewInt(8000000000))
pool.SetGasPrice(big.NewInt(100000000000))
validate()
}

Expand Down Expand Up @@ -1377,20 +1381,20 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
local, _ := crypto.GenerateKey()
remote, _ := crypto.GenerateKey()

pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(900000000000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(900000000000000))

// Add three local and a remote transactions and ensure they are queued up
if err := pool.AddLocal(pricedTransaction(0, 0, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(0, 0, 100000, big.NewInt(1000000000), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
if err := pool.AddLocal(pricedTransaction(0, 1, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(0, 1, 100000, big.NewInt(1000000000), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
if err := pool.AddLocal(pricedTransaction(0, 2, 100000, big.NewInt(1), local)); err != nil {
if err := pool.AddLocal(pricedTransaction(0, 2, 100000, big.NewInt(1000000000), local)); err != nil {
t.Fatalf("failed to add local transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(0, 0, 100000, big.NewInt(1), remote)); err != nil {
if err := pool.AddRemote(pricedTransaction(0, 0, 100000, big.NewInt(1000000000), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
pending, queued := pool.Stats()
Expand Down Expand Up @@ -1471,15 +1475,15 @@ func TestTransactionStatusCheck(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 3)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(9000000000000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.PoolTransactions{}

txs = append(txs, pricedTransaction(0, 0, 100000, big.NewInt(1), keys[0])) // Pending only
txs = append(txs, pricedTransaction(0, 0, 100000, big.NewInt(1), keys[1])) // Pending and queued
txs = append(txs, pricedTransaction(0, 2, 100000, big.NewInt(1), keys[1]))
txs = append(txs, pricedTransaction(0, 2, 100000, big.NewInt(1), keys[2])) // Queued only
txs = append(txs, pricedTransaction(0, 0, 100000, big.NewInt(1000000000), keys[0])) // Pending only
txs = append(txs, pricedTransaction(0, 0, 100000, big.NewInt(1000000000), keys[1])) // Pending and queued
txs = append(txs, pricedTransaction(0, 2, 100000, big.NewInt(1000000000), keys[1]))
txs = append(txs, pricedTransaction(0, 2, 100000, big.NewInt(1000000000), keys[2])) // Queued only

// Import the transaction and ensure they are correctly added
pool.AddRemotes(txs)
Expand Down
6 changes: 4 additions & 2 deletions core/types/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
)

const (
//MaxPoolTransactionDataSize is a heuristic data limit for DOS prevention 1.25Mb
MaxPoolTransactionDataSize = 1280 * 1024
//MaxP2PNodeDataSize is a 1.25Mb heuristic data limit for DOS prevention on node message
MaxP2PNodeDataSize = 1280 * 1024
//MaxPoolTransactionDataSize is a 128KB heuristic data limit for DOS prevention on txn
MaxPoolTransactionDataSize = 128 * 1024
//MaxEncodedPoolTransactionSize is a heuristic raw/encoded data size limit. It has an additional 10KB for metadata
MaxEncodedPoolTransactionSize = MaxPoolTransactionDataSize + (10 * 1024)
)
Expand Down
2 changes: 1 addition & 1 deletion node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ func (node *Node) validateNodeMessage(ctx context.Context, payload []byte) (
// length of payload must > p2pNodeMsgPrefixSize

// reject huge node messages
if len(payload) >= types.MaxEncodedPoolTransactionSize {
if len(payload) >= types.MaxP2PNodeDataSize {
nodeNodeMessageCounterVec.With(prometheus.Labels{"type": "invalid_oversized"}).Inc()
return nil, 0, core.ErrOversizedData
}
Expand Down
Loading