Skip to content

Commit 100cf0a

Browse files
authored
fix/remove-malleated-tx-cache (#834)
* feat: add method in cache to remove txns by key(hash) * fix: remove original tx from cache if current tx is malleated * chore: remove redundant type conversion * chore: typo * test: add cache test for removing tx by key * test: fix check for cache map and list length
1 parent 08c0c03 commit 100cf0a

File tree

3 files changed

+61
-24
lines changed

3 files changed

+61
-24
lines changed

mempool/cache.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ type TxCache interface {
2323
// Remove removes the given raw transaction from the cache.
2424
Remove(tx types.Tx)
2525

26+
// RemoveTxByKey removes a given transaction hash from the cache.
27+
RemoveTxByKey(key types.TxKey)
28+
2629
// Has reports whether tx is present in the cache. Checking for presence is
2730
// not treated as an access of the value.
2831
Has(tx types.Tx) bool
@@ -89,10 +92,14 @@ func (c *LRUTxCache) Push(tx types.Tx) bool {
8992
}
9093

9194
func (c *LRUTxCache) Remove(tx types.Tx) {
95+
key := tx.Key()
96+
c.RemoveTxByKey(key)
97+
}
98+
99+
func (c *LRUTxCache) RemoveTxByKey(key types.TxKey) {
92100
c.mtx.Lock()
93101
defer c.mtx.Unlock()
94102

95-
key := tx.Key()
96103
e := c.cacheMap[key]
97104
delete(c.cacheMap, key)
98105

@@ -114,7 +121,8 @@ type NopTxCache struct{}
114121

115122
var _ TxCache = (*NopTxCache)(nil)
116123

117-
func (NopTxCache) Reset() {}
118-
func (NopTxCache) Push(types.Tx) bool { return true }
119-
func (NopTxCache) Remove(types.Tx) {}
120-
func (NopTxCache) Has(types.Tx) bool { return false }
124+
func (NopTxCache) Reset() {}
125+
func (NopTxCache) Push(types.Tx) bool { return true }
126+
func (NopTxCache) Remove(types.Tx) {}
127+
func (NopTxCache) RemoveTxByKey(key types.TxKey) {}
128+
func (NopTxCache) Has(types.Tx) bool { return false }

mempool/cache_test.go

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,58 @@ import (
55
"testing"
66

77
"github.com/stretchr/testify/require"
8+
"github.com/tendermint/tendermint/types"
89
)
910

10-
func TestCacheRemove(t *testing.T) {
11-
cache := NewLRUTxCache(100)
12-
numTxs := 10
11+
func populate(cache TxCache, numTxs int) ([][]byte, error) {
1312

1413
txs := make([][]byte, numTxs)
1514
for i := 0; i < numTxs; i++ {
1615
// probability of collision is 2**-256
1716
txBytes := make([]byte, 32)
1817
_, err := rand.Read(txBytes)
19-
require.NoError(t, err)
18+
19+
if err != nil {
20+
return nil, err
21+
}
2022

2123
txs[i] = txBytes
2224
cache.Push(txBytes)
23-
24-
// make sure its added to both the linked list and the map
25-
require.Equal(t, i+1, len(cache.cacheMap))
26-
require.Equal(t, i+1, cache.list.Len())
2725
}
2826

27+
return txs, nil
28+
}
29+
30+
func TestCacheRemove(t *testing.T) {
31+
cache := NewLRUTxCache(100)
32+
numTxs := 10
33+
34+
txs, err := populate(cache, numTxs)
35+
require.NoError(t, err)
36+
require.Equal(t, numTxs, len(cache.cacheMap))
37+
require.Equal(t, numTxs, cache.list.Len())
38+
2939
for i := 0; i < numTxs; i++ {
3040
cache.Remove(txs[i])
3141
// make sure its removed from both the map and the linked list
3242
require.Equal(t, numTxs-(i+1), len(cache.cacheMap))
3343
require.Equal(t, numTxs-(i+1), cache.list.Len())
3444
}
3545
}
46+
47+
func TestCacheRemoveByKey(t *testing.T) {
48+
cache := NewLRUTxCache(100)
49+
numTxs := 10
50+
51+
txs, err := populate(cache, numTxs)
52+
require.NoError(t, err)
53+
require.Equal(t, numTxs, len(cache.cacheMap))
54+
require.Equal(t, numTxs, cache.list.Len())
55+
56+
for i := 0; i < numTxs; i++ {
57+
cache.RemoveTxByKey(types.Tx(txs[i]).Key())
58+
// make sure its removed from both the map and the linked list
59+
require.Equal(t, numTxs-(i+1), len(cache.cacheMap))
60+
require.Equal(t, numTxs-(i+1), cache.list.Len())
61+
}
62+
}

mempool/v1/mempool.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package v1
22

33
import (
4-
"crypto/sha256"
54
"fmt"
65
"runtime"
76
"sort"
@@ -401,22 +400,25 @@ func (txmp *TxMempool) Update(
401400
}
402401

403402
for i, tx := range blockTxs {
403+
// The tx hash corresponding to the originating transaction. Set to the
404+
// current tx hash initially, but can change in case of a malleated tx.
405+
originalKey := tx.Key()
406+
407+
// Regardless of outcome, remove the transaction from the mempool.
408+
if err := txmp.removeTxByKey(originalKey); err != nil {
409+
if originalHash, _, isMalleated := types.UnwrapMalleatedTx(tx); isMalleated {
410+
copy(originalKey[:], originalHash)
411+
_ = txmp.removeTxByKey(originalKey)
412+
}
413+
}
414+
404415
// Add successful committed transactions to the cache (if they are not
405416
// already present). Transactions that failed to commit are removed from
406417
// the cache unless the operator has explicitly requested we keep them.
407418
if deliverTxResponses[i].Code == abci.CodeTypeOK {
408419
_ = txmp.cache.Push(tx)
409420
} else if !txmp.config.KeepInvalidTxsInCache {
410-
txmp.cache.Remove(tx)
411-
}
412-
413-
// Regardless of success, remove the transaction from the mempool.
414-
if err := txmp.removeTxByKey(tx.Key()); err != nil {
415-
if originalHash, _, isMalleated := types.UnwrapMalleatedTx(tx); isMalleated {
416-
var originalKey [sha256.Size]byte
417-
copy(originalKey[:], originalHash)
418-
_ = txmp.removeTxByKey(types.TxKey(originalKey))
419-
}
421+
txmp.cache.RemoveTxByKey(originalKey)
420422
}
421423
}
422424

0 commit comments

Comments
 (0)