Browse Source

mempool: introduce KeepInvalidTxsInCache config option (#5813)

When set to true, an invalid transaction will be kept in the cache (this may help some applications to protect against spam).

NOTE: this is a temporary config option. The more correct solution would be to add a TTL to each transaction (i.e. CheckTx may return a TTL in ResponseCheckTx).

Closes: #5751
pull/5808/head
Anton Kaliaev 4 years ago
committed by GitHub
parent
commit
6a056e050c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 9 deletions
  1. +1
    -0
      CHANGELOG_PENDING.md
  2. +4
    -0
      config/config.go
  3. +5
    -0
      config/toml.go
  4. +10
    -5
      docs/nodes/configuration.md
  5. +6
    -4
      mempool/clist_mempool.go
  6. +57
    -0
      mempool/clist_mempool_test.go

+ 1
- 0
CHANGELOG_PENDING.md View File

@ -44,6 +44,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
- [cli] \#5772 `gen_node_key` output now contains node ID (`id` field) (@melekes) - [cli] \#5772 `gen_node_key` output now contains node ID (`id` field) (@melekes)
- [blockchain/v2] \#5774 Send status request when new peer joins (@melekes) - [blockchain/v2] \#5774 Send status request when new peer joins (@melekes)
- [consensus] \#5792 Deprecates the `time_iota_ms` consensus parameter, to reduce the bug surface. The parameter is no longer used. (@valardragon) - [consensus] \#5792 Deprecates the `time_iota_ms` consensus parameter, to reduce the bug surface. The parameter is no longer used. (@valardragon)
- [mempool] \#5813 Add `keep-invalid-txs-in-cache` config option. When set to true, mempool will keep invalid transactions in the cache (@p4u)
### BUG FIXES ### BUG FIXES


+ 4
- 0
config/config.go View File

@ -636,6 +636,10 @@ type MempoolConfig struct {
MaxTxsBytes int64 `mapstructure:"max-txs-bytes"` MaxTxsBytes int64 `mapstructure:"max-txs-bytes"`
// Size of the cache (used to filter transactions we saw earlier) in transactions // Size of the cache (used to filter transactions we saw earlier) in transactions
CacheSize int `mapstructure:"cache-size"` CacheSize int `mapstructure:"cache-size"`
// Do not remove invalid transactions from the cache (default: false)
// Set to true if it's not possible for any invalid transaction to become
// valid again in the future.
KeepInvalidTxsInCache bool `mapstructure:"keep-invalid-txs-in-cache"`
// Maximum size of a single transaction // Maximum size of a single transaction
// NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}. // NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}.
MaxTxBytes int `mapstructure:"max-tx-bytes"` MaxTxBytes int `mapstructure:"max-tx-bytes"`


+ 5
- 0
config/toml.go View File

@ -330,6 +330,11 @@ max-txs-bytes = {{ .Mempool.MaxTxsBytes }}
# Size of the cache (used to filter transactions we saw earlier) in transactions # Size of the cache (used to filter transactions we saw earlier) in transactions
cache-size = {{ .Mempool.CacheSize }} cache-size = {{ .Mempool.CacheSize }}
# Do not remove invalid transactions from the cache (default: false)
# Set to true if it's not possible for any invalid transaction to become valid
# again in the future.
keep-invalid-txs-in-cache = {{ .Mempool.KeepInvalidTxsInCache }}
# Maximum size of a single transaction. # Maximum size of a single transaction.
# NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}. # NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}.
max-tx-bytes = {{ .Mempool.MaxTxBytes }} max-tx-bytes = {{ .Mempool.MaxTxBytes }}


+ 10
- 5
docs/nodes/configuration.md View File

@ -278,6 +278,11 @@ max-txs-bytes = 1073741824
# Size of the cache (used to filter transactions we saw earlier) in transactions # Size of the cache (used to filter transactions we saw earlier) in transactions
cache-size = 10000 cache-size = 10000
# Do not remove invalid transactions from the cache (default: false)
# Set to true if it's not possible for any invalid transaction to become valid
# again in the future.
keep-invalid-txs-in-cache = false
# Maximum size of a single transaction. # Maximum size of a single transaction.
# NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}. # NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}.
max-tx-bytes = 1048576 max-tx-bytes = 1048576
@ -476,16 +481,16 @@ Here's a brief summary of the timeouts:
on the new height (this gives us a chance to receive some more precommits, on the new height (this gives us a chance to receive some more precommits,
even though we already have +2/3) even though we already have +2/3)
## P2P settings
## P2P settings
This section will cover settings within the p2p section of the `config.toml`.
This section will cover settings within the p2p section of the `config.toml`.
- `external-address` = is the address that will be advertised for other nodes to use. We recommend setting this field with your public IP and p2p port.
- `external-address` = is the address that will be advertised for other nodes to use. We recommend setting this field with your public IP and p2p port.
- `seeds` = is a list of comma separated seed nodes that you will connect upon a start and ask for peers. A seed node is a node that does not participate in consensus but only helps propagate peers to nodes in the networks - `seeds` = is a list of comma separated seed nodes that you will connect upon a start and ask for peers. A seed node is a node that does not participate in consensus but only helps propagate peers to nodes in the networks
- `persistent-peers` = is a list of comma separated peers that you will always want to be connected to. If you're already connected to the maximum number of peers, persistent peers will not be added.
- `persistent-peers` = is a list of comma separated peers that you will always want to be connected to. If you're already connected to the maximum number of peers, persistent peers will not be added.
- `max-num-inbound-peers` = is the maximum number of peers you will accept inbound connections from at one time (where they dial your address and initiate the connection). - `max-num-inbound-peers` = is the maximum number of peers you will accept inbound connections from at one time (where they dial your address and initiate the connection).
- `max-num-outbound-peers` = is the maximum number of peers you will initiate outbound connects to at one time (where you dial their address and initiate the connection). - `max-num-outbound-peers` = is the maximum number of peers you will initiate outbound connects to at one time (where you dial their address and initiate the connection).
- `unconditional-peer-ids` = is similar to `persistent-peers` except that these peers will be connected to even if you are already connected to the maximum number of peers. This can be a validator node ID on your sentry node. - `unconditional-peer-ids` = is similar to `persistent-peers` except that these peers will be connected to even if you are already connected to the maximum number of peers. This can be a validator node ID on your sentry node.
- `pex` = turns the peer exchange reactor on or off. Validator node will want the `pex` turned off so it would not begin gossiping to unknown peers on the network. PeX can also be turned off for statically configured networks with fixed network connectivity. For full nodes on open, dynamic networks, it should be turned on. - `pex` = turns the peer exchange reactor on or off. Validator node will want the `pex` turned off so it would not begin gossiping to unknown peers on the network. PeX can also be turned off for statically configured networks with fixed network connectivity. For full nodes on open, dynamic networks, it should be turned on.
- `seed-mode` = is used for when node operators want to run their node as a seed node. Seed node's run a variation of the PeX protocol that disconnects from peers after sending them a list of peers to connect to. To minimize the servers usage, it is recommended to set the mempool's size to 0. - `seed-mode` = is used for when node operators want to run their node as a seed node. Seed node's run a variation of the PeX protocol that disconnects from peers after sending them a list of peers to connect to. To minimize the servers usage, it is recommended to set the mempool's size to 0.
- `private-peer-ids` = is a comma separated list of node ids that you would not like exposed to other peers (ie. you will not tell other peers about the private-peer-ids). This can be filled with a validators node id.
- `private-peer-ids` = is a comma separated list of node ids that you would not like exposed to other peers (ie. you will not tell other peers about the private-peer-ids). This can be filled with a validators node id.

+ 6
- 4
mempool/clist_mempool.go View File

@ -449,8 +449,10 @@ func (mem *CListMempool) resCbFirstTime(
mem.logger.Info("Rejected bad transaction", mem.logger.Info("Rejected bad transaction",
"tx", txID(tx), "peerID", peerP2PID, "res", r, "err", postCheckErr) "tx", txID(tx), "peerID", peerP2PID, "res", r, "err", postCheckErr)
mem.metrics.FailedTxs.Add(1) mem.metrics.FailedTxs.Add(1)
// remove from cache (it might be good later)
mem.cache.Remove(tx)
if !mem.config.KeepInvalidTxsInCache {
// remove from cache (it might be good later)
mem.cache.Remove(tx)
}
} }
default: default:
// ignore other messages // ignore other messages
@ -482,7 +484,7 @@ func (mem *CListMempool) resCbRecheck(req *abci.Request, res *abci.Response) {
// Tx became invalidated due to newly committed block. // Tx became invalidated due to newly committed block.
mem.logger.Info("Tx is no longer valid", "tx", txID(tx), "res", r, "err", postCheckErr) mem.logger.Info("Tx is no longer valid", "tx", txID(tx), "res", r, "err", postCheckErr)
// NOTE: we remove tx from the cache because it might be good later // NOTE: we remove tx from the cache because it might be good later
mem.removeTx(tx, mem.recheckCursor, true)
mem.removeTx(tx, mem.recheckCursor, !mem.config.KeepInvalidTxsInCache)
} }
if mem.recheckCursor == mem.recheckEnd { if mem.recheckCursor == mem.recheckEnd {
mem.recheckCursor = nil mem.recheckCursor = nil
@ -596,7 +598,7 @@ func (mem *CListMempool) Update(
if deliverTxResponses[i].Code == abci.CodeTypeOK { if deliverTxResponses[i].Code == abci.CodeTypeOK {
// Add valid committed tx to the cache (if missing). // Add valid committed tx to the cache (if missing).
_ = mem.cache.Push(tx) _ = mem.cache.Push(tx)
} else {
} else if !mem.config.KeepInvalidTxsInCache {
// Allow invalid transactions to be resubmitted. // Allow invalid transactions to be resubmitted.
mem.cache.Remove(tx) mem.cache.Remove(tx)
} }


+ 57
- 0
mempool/clist_mempool_test.go View File

@ -217,6 +217,63 @@ func TestMempoolUpdate(t *testing.T) {
} }
} }
func TestMempool_KeepInvalidTxsInCache(t *testing.T) {
app := counter.NewApplication(true)
cc := proxy.NewLocalClientCreator(app)
wcfg := cfg.DefaultConfig()
wcfg.Mempool.KeepInvalidTxsInCache = true
mempool, cleanup := newMempoolWithAppAndConfig(cc, wcfg)
defer cleanup()
// 1. An invalid transaction must remain in the cache after Update
{
a := make([]byte, 8)
binary.BigEndian.PutUint64(a, 0)
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, 1)
err := mempool.CheckTx(b, nil, TxInfo{})
require.NoError(t, err)
// simulate new block
_ = app.DeliverTx(abci.RequestDeliverTx{Tx: a})
_ = app.DeliverTx(abci.RequestDeliverTx{Tx: b})
err = mempool.Update(1, []types.Tx{a, b},
[]*abci.ResponseDeliverTx{{Code: abci.CodeTypeOK}, {Code: 2}}, nil, nil)
require.NoError(t, err)
// a must be added to the cache
err = mempool.CheckTx(a, nil, TxInfo{})
if assert.Error(t, err) {
assert.Equal(t, ErrTxInCache, err)
}
// b must remain in the cache
err = mempool.CheckTx(b, nil, TxInfo{})
if assert.Error(t, err) {
assert.Equal(t, ErrTxInCache, err)
}
}
// 2. An invalid transaction must remain in the cache
{
a := make([]byte, 8)
binary.BigEndian.PutUint64(a, 0)
// remove a from the cache to test (2)
mempool.cache.Remove(a)
err := mempool.CheckTx(a, nil, TxInfo{})
require.NoError(t, err)
err = mempool.CheckTx(a, nil, TxInfo{})
if assert.Error(t, err) {
assert.Equal(t, ErrTxInCache, err)
}
}
}
func TestTxsAvailable(t *testing.T) { func TestTxsAvailable(t *testing.T) {
app := kvstore.NewApplication() app := kvstore.NewApplication()
cc := proxy.NewLocalClientCreator(app) cc := proxy.NewLocalClientCreator(app)


Loading…
Cancel
Save