Skip to content
This repository was archived by the owner on Jan 31, 2025. It is now read-only.

Commit 70b5d4c

Browse files
feat: Cache Tx Decoder (backport #528) (#531)
* feat: Cache Tx Decoder (#528) * init * nit * nits * nit * more nits * go version bump * image bump * nit (cherry picked from commit bfdd584) # Conflicts: # abci/checktx/check_tx_test.go # go.mod # tests/app/app.go # tests/e2e/go.mod # tests/e2e/go.sum * merge --------- Co-authored-by: David Terpay <35130517+davidterpay@users.noreply.github.com> Co-authored-by: David Terpay <david.terpay@gmail.com>
1 parent ca45053 commit 70b5d4c

File tree

13 files changed

+519
-148
lines changed

13 files changed

+519
-148
lines changed

.github/workflows/lint.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
steps:
1919
- uses: actions/setup-go@v5
2020
with:
21-
go-version: 1.21.5
21+
go-version: 1.22.4
2222
- uses: actions/checkout@v4
2323
- name: golangci-lint
2424
uses: golangci/golangci-lint-action@v4
@@ -32,7 +32,7 @@ jobs:
3232
- uses: actions/checkout@v4
3333
- uses: actions/setup-go@v5
3434
with:
35-
go-version: 1.21.5
35+
go-version: 1.22.4
3636
cache: true
3737
cache-dependency-path: go.sum
3838
- uses: technote-space/get-diff-action@v6.1.2

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- name: Install Go
2121
uses: actions/setup-go@v5
2222
with:
23-
go-version: 1.21.5
23+
go-version: 1.22.4
2424
- name: Unshallow
2525
run: git fetch --prune --unshallow
2626
- name: Create release

.github/workflows/test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- uses: actions/checkout@v4
2121
- uses: actions/setup-go@v5
2222
with:
23-
go-version: 1.21.5
23+
go-version: 1.22.4
2424
cache: true
2525
cache-dependency-path: go.sum
2626
- uses: technote-space/get-diff-action@v6.1.2
@@ -52,7 +52,7 @@ jobs:
5252
- uses: actions/checkout@v4
5353
- uses: actions/setup-go@v5
5454
with:
55-
go-version: 1.21.5
55+
go-version: 1.22.4
5656
cache: true
5757
cache-dependency-path: go.sum
5858
- uses: technote-space/get-diff-action@v6.1.2
@@ -82,7 +82,7 @@ jobs:
8282
- uses: actions/setup-go@v5
8383
if: env.GIT_DIFF
8484
with:
85-
go-version: 1.21.5
85+
go-version: 1.22.4
8686
cache: true
8787

8888
# In this step, this action saves a list of existing images, the cache is

abci/checktx/check_tx_test.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/cosmos/cosmos-sdk/store"
1919
storetypes "github.com/cosmos/cosmos-sdk/store/types"
2020

21+
"github.com/skip-mev/block-sdk/block/utils"
2122
mevlanetestutils "github.com/skip-mev/block-sdk/lanes/mev/testutils"
2223
"github.com/skip-mev/block-sdk/testutils"
2324
auctiontypes "github.com/skip-mev/block-sdk/x/auction/types"
@@ -63,12 +64,15 @@ func (s *CheckTxTestSuite) TestCheckTxMempoolParity() {
6364
mempool, err := block.NewLanedMempool(s.Ctx.Logger(), []block.Lane{mevLane})
6465
s.Require().NoError(err)
6566

67+
cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
68+
s.Require().NoError(err)
69+
6670
ba := &baseApp{
6771
s.Ctx,
6872
}
6973
mevLaneHandler := checktx.NewMEVCheckTxHandler(
7074
ba,
71-
s.EncCfg.TxConfig.TxDecoder(),
75+
cacheDecoder.TxDecoder(),
7276
mevLane,
7377
s.SetUpAnteHandler(txs),
7478
ba.CheckTx,
@@ -78,7 +82,7 @@ func (s *CheckTxTestSuite) TestCheckTxMempoolParity() {
7882
handler := checktx.NewMempoolParityCheckTx(
7983
s.Ctx.Logger(),
8084
mempool,
81-
s.EncCfg.TxConfig.TxDecoder(),
85+
cacheDecoder.TxDecoder(),
8286
mevLaneHandler,
8387
).CheckTx()
8488

@@ -128,10 +132,13 @@ func (s *CheckTxTestSuite) TestRemovalOnRecheckTx() {
128132
mempool, err := block.NewLanedMempool(s.Ctx.Logger(), []block.Lane{mevLane})
129133
s.Require().NoError(err)
130134

135+
cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
136+
s.Require().NoError(err)
137+
131138
handler := checktx.NewMempoolParityCheckTx(
132139
s.Ctx.Logger(),
133140
mempool,
134-
s.EncCfg.TxConfig.TxDecoder(),
141+
cacheDecoder.TxDecoder(),
135142
func(cometabci.RequestCheckTx) cometabci.ResponseCheckTx {
136143
// always fail
137144
return cometabci.ResponseCheckTx{Code: 1}
@@ -159,10 +166,13 @@ func (s *CheckTxTestSuite) TestRemovalOnRecheckTx() {
159166

160167
func (s *CheckTxTestSuite) TestMempoolParityCheckTx() {
161168
s.Run("tx fails tx-decoding", func() {
169+
cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
170+
s.Require().NoError(err)
171+
162172
handler := checktx.NewMempoolParityCheckTx(
163173
s.Ctx.Logger(),
164174
nil,
165-
s.EncCfg.TxConfig.TxDecoder(),
175+
cacheDecoder.TxDecoder(),
166176
nil,
167177
)
168178

@@ -188,10 +198,13 @@ func (s *CheckTxTestSuite) TestMEVCheckTxHandler() {
188198
normalTx, err := testutils.CreateRandomTxBz(s.EncCfg.TxConfig, acc, 0, 1, 0, 0)
189199
s.Require().NoError(err)
190200

201+
cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
202+
s.Require().NoError(err)
203+
191204
var gotTx []byte
192205
mevLaneHandler := checktx.NewMEVCheckTxHandler(
193206
ba,
194-
s.EncCfg.TxConfig.TxDecoder(),
207+
cacheDecoder.TxDecoder(),
195208
mevLane,
196209
s.SetUpAnteHandler(txs),
197210
func(req cometabci.RequestCheckTx) cometabci.ResponseCheckTx {
@@ -207,7 +220,7 @@ func (s *CheckTxTestSuite) TestMEVCheckTxHandler() {
207220
handler := checktx.NewMempoolParityCheckTx(
208221
s.Ctx.Logger(),
209222
mempool,
210-
s.EncCfg.TxConfig.TxDecoder(),
223+
cacheDecoder.TxDecoder(),
211224
mevLaneHandler,
212225
).CheckTx()
213226

@@ -275,12 +288,15 @@ func (s *CheckTxTestSuite) TestValidateBidTx() {
275288

276289
mevLane := s.InitLane(math.LegacyOneDec(), txs)
277290

291+
cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
292+
s.Require().NoError(err)
293+
278294
ba := &baseApp{
279295
s.Ctx,
280296
}
281297
mevLaneHandler := checktx.NewMEVCheckTxHandler(
282298
ba,
283-
s.EncCfg.TxConfig.TxDecoder(),
299+
cacheDecoder.TxDecoder(),
284300
mevLane,
285301
s.SetUpAnteHandler(txs),
286302
ba.CheckTx,

abci/checktx/mempool_parity_check_tx.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ type MempoolParityCheckTx struct {
2929
}
3030

3131
// NewMempoolParityCheckTx returns a new MempoolParityCheckTx handler.
32-
func NewMempoolParityCheckTx(logger log.Logger, mempl block.Mempool, txDecoder sdk.TxDecoder, checkTxHandler CheckTx) MempoolParityCheckTx {
32+
func NewMempoolParityCheckTx(
33+
logger log.Logger,
34+
mempl block.Mempool,
35+
txDecoder sdk.TxDecoder,
36+
checkTxHandler CheckTx,
37+
) MempoolParityCheckTx {
3338
return MempoolParityCheckTx{
3439
logger: logger,
3540
mempl: mempl,

block/utils/decoder.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package utils
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
7+
sdk "github.com/cosmos/cosmos-sdk/types"
8+
)
9+
10+
// DefaultMaxSize is the default maximum size of the cache.
11+
var DefaultMaxSize uint64 = 500
12+
13+
// CacheTxDecoder wraps the sdk.TxDecoder and caches the decoded transactions with
14+
// an LRU'esque cache. Each transaction is cached using the transaction's hash
15+
// as the key. The cache is purged when the number of transactions in the cache
16+
// exceeds the maximum size. The oldest transactions are removed first.
17+
type CacheTxDecoder struct {
18+
mut sync.Mutex
19+
20+
decoder sdk.TxDecoder
21+
cache map[string]sdk.Tx
22+
window []string
23+
insertIndex int
24+
oldestIndex int
25+
maxSize uint64
26+
}
27+
28+
// NewDefaultCacheTxDecoder returns a new CacheTxDecoder.
29+
func NewDefaultCacheTxDecoder(
30+
decoder sdk.TxDecoder,
31+
) (*CacheTxDecoder, error) {
32+
if decoder == nil {
33+
return nil, fmt.Errorf("decoder cannot be nil")
34+
}
35+
36+
return &CacheTxDecoder{
37+
decoder: decoder,
38+
cache: make(map[string]sdk.Tx),
39+
window: make([]string, DefaultMaxSize),
40+
insertIndex: 0,
41+
oldestIndex: 0,
42+
maxSize: DefaultMaxSize,
43+
}, nil
44+
}
45+
46+
// NewCacheTxDecoder returns a new CacheTxDecoder with the given cache interval.
47+
func NewCacheTxDecoder(
48+
decoder sdk.TxDecoder,
49+
maxSize uint64,
50+
) (*CacheTxDecoder, error) {
51+
if decoder == nil {
52+
return nil, fmt.Errorf("decoder cannot be nil")
53+
}
54+
55+
return &CacheTxDecoder{
56+
decoder: decoder,
57+
cache: make(map[string]sdk.Tx),
58+
window: make([]string, maxSize),
59+
insertIndex: 0,
60+
oldestIndex: 0,
61+
maxSize: maxSize,
62+
}, nil
63+
}
64+
65+
// Decode decodes the transaction bytes into a sdk.Tx. It caches the decoded
66+
// transaction using the transaction's hash as the key.
67+
func (ctd *CacheTxDecoder) TxDecoder() sdk.TxDecoder {
68+
return func(txBytes []byte) (sdk.Tx, error) {
69+
ctd.mut.Lock()
70+
defer ctd.mut.Unlock()
71+
72+
hash := TxHash(txBytes)
73+
if tx, ok := ctd.cache[hash]; ok {
74+
return tx, nil
75+
}
76+
77+
tx, err := ctd.decoder(txBytes)
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
// Purge the cache if necessary
83+
if uint64(len(ctd.cache)) >= ctd.maxSize {
84+
// Purge the oldest transaction
85+
entry := ctd.window[ctd.oldestIndex]
86+
delete(ctd.cache, entry)
87+
88+
// Increment the oldest index
89+
ctd.oldestIndex++
90+
ctd.oldestIndex %= int(ctd.maxSize)
91+
}
92+
93+
// Update the cache and window
94+
ctd.cache[hash] = tx
95+
ctd.window[ctd.insertIndex] = hash
96+
97+
// Increment the insert index
98+
ctd.insertIndex++
99+
ctd.insertIndex %= int(ctd.maxSize)
100+
101+
return tx, nil
102+
}
103+
}
104+
105+
// Len returns the number of transactions in the cache.
106+
func (ctd *CacheTxDecoder) Len() int {
107+
ctd.mut.Lock()
108+
defer ctd.mut.Unlock()
109+
110+
return len(ctd.cache)
111+
}
112+
113+
// Contains returns true if the cache contains the transaction with the given hash.
114+
func (ctd *CacheTxDecoder) Contains(txBytes []byte) bool {
115+
ctd.mut.Lock()
116+
defer ctd.mut.Unlock()
117+
118+
hash := TxHash(txBytes)
119+
_, ok := ctd.cache[hash]
120+
return ok
121+
}

0 commit comments

Comments
 (0)