Skip to content

Commit

Permalink
feat: Cache Tx Decoder (#528)
Browse files Browse the repository at this point in the history
* init

* nit

* nits

* nit

* more nits

* go version bump

* image bump

* nit

(cherry picked from commit bfdd584)

# Conflicts:
#	abci/checktx/check_tx_test.go
#	tests/app/app.go
#	tests/e2e/go.mod
#	tests/e2e/go.sum
  • Loading branch information
davidterpay authored and mergify[bot] committed Jun 20, 2024
1 parent f154ba5 commit 94dfd13
Show file tree
Hide file tree
Showing 13 changed files with 553 additions and 124 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
steps:
- uses: actions/setup-go@v5
with:
go-version: 1.21.5
go-version: 1.22.4
- uses: actions/checkout@v4
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
Expand All @@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.21.5
go-version: 1.22.4
cache: true
cache-dependency-path: go.sum
- uses: technote-space/[email protected]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21.5
go-version: 1.22.4
- name: Unshallow
run: git fetch --prune --unshallow
- name: Create release
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.21.5
go-version: 1.22.4
cache: true
cache-dependency-path: go.sum
- uses: technote-space/[email protected]
Expand Down Expand Up @@ -52,7 +52,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.21.5
go-version: 1.22.4
cache: true
cache-dependency-path: go.sum
- uses: technote-space/[email protected]
Expand Down Expand Up @@ -82,7 +82,7 @@ jobs:
- uses: actions/setup-go@v5
if: env.GIT_DIFF
with:
go-version: 1.21.5
go-version: 1.22.4
cache: true

# In this step, this action saves a list of existing images, the cache is
Expand Down
34 changes: 27 additions & 7 deletions abci/checktx/check_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import (

"github.com/skip-mev/block-sdk/v2/abci/checktx"
"github.com/skip-mev/block-sdk/v2/block"
<<<<<<< HEAD

Check failure on line 20 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / test-integration

missing import path

Check failure on line 20 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / test-unit

missing import path

Check failure on line 20 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / govulncheck

missing import path

Check failure on line 20 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

missing import path (typecheck)

Check failure on line 20 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

syntax error: missing import path
=======

Check failure on line 21 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / test-integration

missing import path

Check failure on line 21 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

syntax error: missing import path
"github.com/skip-mev/block-sdk/v2/block/utils"
"github.com/skip-mev/block-sdk/v2/lanes/mev"
>>>>>>> bfdd584 (feat: Cache Tx Decoder (#528))

Check failure on line 24 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / test-integration

missing import path

Check failure on line 24 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / test-integration

illegal character U+0023 '#'

Check failure on line 24 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

syntax error: missing import path

Check failure on line 24 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

invalid character U+0023 '#' (typecheck)
mevlanetestutils "github.com/skip-mev/block-sdk/v2/lanes/mev/testutils"
"github.com/skip-mev/block-sdk/v2/testutils"
auctiontypes "github.com/skip-mev/block-sdk/v2/x/auction/types"
Expand Down Expand Up @@ -62,12 +67,15 @@ func (s *CheckTxTestSuite) TestCheckTxMempoolParity() {
mempool, err := block.NewLanedMempool(s.Ctx.Logger(), []block.Lane{mevLane})
s.Require().NoError(err)

cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
s.Require().NoError(err)

ba := &baseApp{
s.Ctx,
}
mevLaneHandler := checktx.NewMEVCheckTxHandler(
ba,
s.EncCfg.TxConfig.TxDecoder(),
cacheDecoder.TxDecoder(),
mevLane,
s.SetUpAnteHandler(txs),
ba.CheckTx,
Expand All @@ -76,7 +84,7 @@ func (s *CheckTxTestSuite) TestCheckTxMempoolParity() {
handler := checktx.NewMempoolParityCheckTx(
s.Ctx.Logger(),
mempool,
s.EncCfg.TxConfig.TxDecoder(),
cacheDecoder.TxDecoder(),
mevLaneHandler,
).CheckTx()

Expand Down Expand Up @@ -128,10 +136,13 @@ func (s *CheckTxTestSuite) TestRemovalOnRecheckTx() {
mempool, err := block.NewLanedMempool(s.Ctx.Logger(), []block.Lane{mevLane})
s.Require().NoError(err)

cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
s.Require().NoError(err)

handler := checktx.NewMempoolParityCheckTx(
s.Ctx.Logger(),
mempool,
s.EncCfg.TxConfig.TxDecoder(),
cacheDecoder.TxDecoder(),
func(*cometabci.RequestCheckTx) (*cometabci.ResponseCheckTx, error) {
// always fail
return &cometabci.ResponseCheckTx{Code: 1}, nil

Check failure on line 148 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / test-integration

expected declaration, found 'return'
Expand Down Expand Up @@ -159,10 +170,13 @@ func (s *CheckTxTestSuite) TestRemovalOnRecheckTx() {

func (s *CheckTxTestSuite) TestMempoolParityCheckTx() {
s.Run("tx fails tx-decoding", func() {
cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
s.Require().NoError(err)

handler := checktx.NewMempoolParityCheckTx(
s.Ctx.Logger(),
nil,
s.EncCfg.TxConfig.TxDecoder(),
cacheDecoder.TxDecoder(),
nil,
)

Expand All @@ -189,10 +203,13 @@ func (s *CheckTxTestSuite) TestMEVCheckTxHandler() {
normalTx, err := testutils.CreateRandomTxBz(s.EncCfg.TxConfig, acc, 0, 1, 0, 0)
s.Require().NoError(err)

cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
s.Require().NoError(err)

var gotTx []byte
mevLaneHandler := checktx.NewMEVCheckTxHandler(

Check failure on line 210 in abci/checktx/check_tx_test.go

View workflow job for this annotation

GitHub Actions / test-integration

expected declaration, found mevLaneHandler
ba,
s.EncCfg.TxConfig.TxDecoder(),
cacheDecoder.TxDecoder(),
mevLane,
s.SetUpAnteHandler(txs),
func(req *cometabci.RequestCheckTx) (*cometabci.ResponseCheckTx, error) {
Expand All @@ -207,7 +224,7 @@ func (s *CheckTxTestSuite) TestMEVCheckTxHandler() {
handler := checktx.NewMempoolParityCheckTx(
s.Ctx.Logger(),
mempool,
s.EncCfg.TxConfig.TxDecoder(),
cacheDecoder.TxDecoder(),
mevLaneHandler,
).CheckTx()

Expand Down Expand Up @@ -276,12 +293,15 @@ func (s *CheckTxTestSuite) TestValidateBidTx() {

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

cacheDecoder, err := utils.NewDefaultCacheTxDecoder(s.EncCfg.TxConfig.TxDecoder())
s.Require().NoError(err)

ba := &baseApp{
s.Ctx,
}
mevLaneHandler := checktx.NewMEVCheckTxHandler(
ba,
s.EncCfg.TxConfig.TxDecoder(),
cacheDecoder.TxDecoder(),
mevLane,
s.SetUpAnteHandler(txs),
ba.CheckTx,
Expand Down
7 changes: 6 additions & 1 deletion abci/checktx/mempool_parity_check_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ type MempoolParityCheckTx struct {
}

// NewMempoolParityCheckTx returns a new MempoolParityCheckTx handler.
func NewMempoolParityCheckTx(logger log.Logger, mempl block.Mempool, txDecoder sdk.TxDecoder, checkTxHandler CheckTx) MempoolParityCheckTx {
func NewMempoolParityCheckTx(
logger log.Logger,
mempl block.Mempool,
txDecoder sdk.TxDecoder,
checkTxHandler CheckTx,
) MempoolParityCheckTx {
return MempoolParityCheckTx{
logger: logger,
mempl: mempl,
Expand Down
121 changes: 121 additions & 0 deletions block/utils/decoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package utils

import (
"fmt"
"sync"

sdk "github.com/cosmos/cosmos-sdk/types"
)

// DefaultMaxSize is the default maximum size of the cache.
var DefaultMaxSize uint64 = 500

// CacheTxDecoder wraps the sdk.TxDecoder and caches the decoded transactions with
// an LRU'esque cache. Each transaction is cached using the transaction's hash
// as the key. The cache is purged when the number of transactions in the cache
// exceeds the maximum size. The oldest transactions are removed first.
type CacheTxDecoder struct {
mut sync.Mutex

decoder sdk.TxDecoder
cache map[string]sdk.Tx
window []string
insertIndex int
oldestIndex int
maxSize uint64
}

// NewDefaultCacheTxDecoder returns a new CacheTxDecoder.
func NewDefaultCacheTxDecoder(
decoder sdk.TxDecoder,
) (*CacheTxDecoder, error) {
if decoder == nil {
return nil, fmt.Errorf("decoder cannot be nil")
}

return &CacheTxDecoder{
decoder: decoder,
cache: make(map[string]sdk.Tx),
window: make([]string, DefaultMaxSize),
insertIndex: 0,
oldestIndex: 0,
maxSize: DefaultMaxSize,
}, nil
}

// NewCacheTxDecoder returns a new CacheTxDecoder with the given cache interval.
func NewCacheTxDecoder(
decoder sdk.TxDecoder,
maxSize uint64,
) (*CacheTxDecoder, error) {
if decoder == nil {
return nil, fmt.Errorf("decoder cannot be nil")
}

return &CacheTxDecoder{
decoder: decoder,
cache: make(map[string]sdk.Tx),
window: make([]string, maxSize),
insertIndex: 0,
oldestIndex: 0,
maxSize: maxSize,
}, nil
}

// Decode decodes the transaction bytes into a sdk.Tx. It caches the decoded
// transaction using the transaction's hash as the key.
func (ctd *CacheTxDecoder) TxDecoder() sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, error) {
ctd.mut.Lock()
defer ctd.mut.Unlock()

hash := TxHash(txBytes)
if tx, ok := ctd.cache[hash]; ok {
return tx, nil
}

tx, err := ctd.decoder(txBytes)
if err != nil {
return nil, err
}

// Purge the cache if necessary
if uint64(len(ctd.cache)) >= ctd.maxSize {
// Purge the oldest transaction
entry := ctd.window[ctd.oldestIndex]
delete(ctd.cache, entry)

// Increment the oldest index
ctd.oldestIndex++
ctd.oldestIndex %= int(ctd.maxSize)
}

// Update the cache and window
ctd.cache[hash] = tx
ctd.window[ctd.insertIndex] = hash

// Increment the insert index
ctd.insertIndex++
ctd.insertIndex %= int(ctd.maxSize)

return tx, nil
}
}

// Len returns the number of transactions in the cache.
func (ctd *CacheTxDecoder) Len() int {
ctd.mut.Lock()
defer ctd.mut.Unlock()

return len(ctd.cache)
}

// Contains returns true if the cache contains the transaction with the given hash.
func (ctd *CacheTxDecoder) Contains(txBytes []byte) bool {
ctd.mut.Lock()
defer ctd.mut.Unlock()

hash := TxHash(txBytes)
_, ok := ctd.cache[hash]
return ok
}
Loading

0 comments on commit 94dfd13

Please sign in to comment.