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

mempool: introduce KeepInvalidTxsInCache config option #5813

Merged
merged 8 commits into from
Dec 21, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- [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)
- [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

Expand Down
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,10 @@ type MempoolConfig struct {
MaxTxsBytes int64 `mapstructure:"max-txs-bytes"`
// Size of the cache (used to filter transactions we saw earlier) in transactions
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
// NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}.
MaxTxBytes int `mapstructure:"max-tx-bytes"`
Expand Down
5 changes: 5 additions & 0 deletions config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,11 @@ max-txs-bytes = {{ .Mempool.MaxTxsBytes }}
# Size of the cache (used to filter transactions we saw earlier) in transactions
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.
# NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}.
max-tx-bytes = {{ .Mempool.MaxTxBytes }}
Expand Down
15 changes: 10 additions & 5 deletions docs/nodes/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@ max-txs-bytes = 1073741824
# Size of the cache (used to filter transactions we saw earlier) in transactions
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.
# NOTE: the max size of a tx transmitted over the network is {max-tx-bytes}.
max-tx-bytes = 1048576
Expand Down Expand Up @@ -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,
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
- `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-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.
- `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.
- `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.
10 changes: 6 additions & 4 deletions mempool/clist_mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,10 @@ func (mem *CListMempool) resCbFirstTime(
mem.logger.Info("Rejected bad transaction",
"tx", txID(tx), "peerID", peerP2PID, "res", r, "err", postCheckErr)
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:
// ignore other messages
Expand Down Expand Up @@ -482,7 +484,7 @@ func (mem *CListMempool) resCbRecheck(req *abci.Request, res *abci.Response) {
// Tx became invalidated due to newly committed block.
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
mem.removeTx(tx, mem.recheckCursor, true)
mem.removeTx(tx, mem.recheckCursor, !mem.config.KeepInvalidTxsInCache)
}
if mem.recheckCursor == mem.recheckEnd {
mem.recheckCursor = nil
Expand Down Expand Up @@ -596,7 +598,7 @@ func (mem *CListMempool) Update(
if deliverTxResponses[i].Code == abci.CodeTypeOK {
// Add valid committed tx to the cache (if missing).
_ = mem.cache.Push(tx)
} else {
} else if !mem.config.KeepInvalidTxsInCache {
// Allow invalid transactions to be resubmitted.
mem.cache.Remove(tx)
}
Expand Down
57 changes: 57 additions & 0 deletions mempool/clist_mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
app := kvstore.NewApplication()
cc := proxy.NewLocalClientCreator(app)
Expand Down