Skip to content

Commit 4cd29a5

Browse files
yihuangmmsqe
authored andcommitted
feat(mempool): respect gas wanted returned by ante handler
* Problem: mempool don't respect gas wanted returned by ante handler Solution: - support custom gas wanted in mempool * Update CHANGELOG.md Signed-off-by: yihuang <[email protected]> * cleanup * cleanup * fix priorityIndex * fix process proposal * fix lint * fix lint --------- Signed-off-by: yihuang <[email protected]> Problem: mempool.GasTx interface is not reused (#536) * Problem: redundant mutex for InsertWithGasWanted cfg of PriorityNonceMempool remains unchanged once assigned, so no lock is required * make mocks * cleanup * keep order of check MaxTx
1 parent c2f3bfd commit 4cd29a5

File tree

11 files changed

+107
-69
lines changed

11 files changed

+107
-69
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
6262
* (baseapp) [#24655](https://github.com/cosmos/cosmos-sdk/pull/24655) Add mutex locks for `state` and make `lastCommitInfo` atomic to prevent race conditions between `Commit` and `CreateQueryContext`.
6363
* (proto) [#24161](https://github.com/cosmos/cosmos-sdk/pull/24161) Remove unnecessary annotations from `x/staking` authz proto.
6464
* (x/bank) [#24660](https://github.com/cosmos/cosmos-sdk/pull/24660) Improve performance of the `GetAllBalances` and `GetAccountsBalances` keeper methods.
65+
* (mempool)[#25338](https://github.com/cosmos/cosmos-sdk/pull/25338) Respect gas wanted returned by ante handler for mempool.
6566

6667
### Bug Fixes
6768

baseapp/abci_utils.go

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ type (
201201
// to verify a transaction.
202202
ProposalTxVerifier interface {
203203
PrepareProposalVerifyTx(tx sdk.Tx) ([]byte, error)
204-
ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, error)
204+
ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, uint64, error)
205205
TxDecode(txBz []byte) (sdk.Tx, error)
206206
TxEncode(tx sdk.Tx) ([]byte, error)
207207
}
@@ -276,7 +276,12 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
276276
return nil, err
277277
}
278278

279-
stop := h.txSelector.SelectTxForProposal(ctx, uint64(req.MaxTxBytes), maxBlockGas, tx, txBz)
279+
var txGasLimit uint64
280+
if gasTx, ok := tx.(mempool.GasTx); ok {
281+
txGasLimit = gasTx.GetGas()
282+
}
283+
284+
stop := h.txSelector.SelectTxForProposal(ctx, uint64(req.MaxTxBytes), maxBlockGas, tx, txBz, txGasLimit)
280285
if stop {
281286
break
282287
}
@@ -291,14 +296,14 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
291296
selectedTxsNums int
292297
invalidTxs []sdk.Tx // invalid txs to be removed out of the loop to avoid dead lock
293298
)
294-
mempool.SelectBy(ctx, h.mempool, req.Txs, func(memTx sdk.Tx) bool {
295-
unorderedTx, ok := memTx.(sdk.TxWithUnordered)
299+
mempool.SelectBy(ctx, h.mempool, req.Txs, func(memTx mempool.Tx) bool {
300+
unorderedTx, ok := memTx.Tx.(sdk.TxWithUnordered)
296301
isUnordered := ok && unorderedTx.GetUnordered()
297302
txSignersSeqs := make(map[string]uint64)
298303

299304
// if the tx is unordered, we don't need to check the sequence, we just add it
300305
if !isUnordered {
301-
signerData, err := h.signerExtAdapter.GetSigners(memTx)
306+
signerData, err := h.signerExtAdapter.GetSigners(memTx.Tx)
302307
if err != nil {
303308
// propagate the error to the caller
304309
resError = err
@@ -333,11 +338,11 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
333338
// which calls mempool.Insert, in theory everything in the pool should be
334339
// valid. But some mempool implementations may insert invalid txs, so we
335340
// check again.
336-
txBz, err := h.txVerifier.PrepareProposalVerifyTx(memTx)
341+
txBz, err := h.txVerifier.PrepareProposalVerifyTx(memTx.Tx)
337342
if err != nil {
338-
invalidTxs = append(invalidTxs, memTx)
343+
invalidTxs = append(invalidTxs, memTx.Tx)
339344
} else {
340-
stop := h.txSelector.SelectTxForProposal(ctx, uint64(req.MaxTxBytes), maxBlockGas, memTx, txBz)
345+
stop := h.txSelector.SelectTxForProposal(ctx, uint64(req.MaxTxBytes), maxBlockGas, memTx.Tx, txBz, memTx.GasWanted)
341346
if stop {
342347
return false
343348
}
@@ -409,17 +414,13 @@ func (h *DefaultProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHan
409414
}
410415

411416
for _, txBytes := range req.Txs {
412-
tx, err := h.txVerifier.ProcessProposalVerifyTx(txBytes)
417+
_, gasWanted, err := h.txVerifier.ProcessProposalVerifyTx(txBytes)
413418
if err != nil {
414419
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
415420
}
416421

417422
if maxBlockGas > 0 {
418-
gasTx, ok := tx.(GasTx)
419-
if ok {
420-
totalTxGas += gasTx.GetGas()
421-
}
422-
423+
totalTxGas += gasWanted
423424
if totalTxGas > uint64(maxBlockGas) {
424425
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
425426
}
@@ -477,7 +478,7 @@ type TxSelector interface {
477478
// a proposal based on inclusion criteria defined by the TxSelector. It must
478479
// return <true> if the caller should halt the transaction selection loop
479480
// (typically over a mempool) or <false> otherwise.
480-
SelectTxForProposal(ctx context.Context, maxTxBytes, maxBlockGas uint64, memTx sdk.Tx, txBz []byte) bool
481+
SelectTxForProposal(ctx context.Context, maxTxBytes, maxBlockGas uint64, memTx sdk.Tx, txBz []byte, gasWanted uint64) bool
481482
}
482483

483484
type defaultTxSelector struct {
@@ -502,23 +503,16 @@ func (ts *defaultTxSelector) Clear() {
502503
ts.selectedTxs = nil
503504
}
504505

505-
func (ts *defaultTxSelector) SelectTxForProposal(_ context.Context, maxTxBytes, maxBlockGas uint64, memTx sdk.Tx, txBz []byte) bool {
506+
func (ts *defaultTxSelector) SelectTxForProposal(_ context.Context, maxTxBytes, maxBlockGas uint64, memTx sdk.Tx, txBz []byte, gasWanted uint64) bool {
506507
txSize := uint64(cmttypes.ComputeProtoSizeForTxs([]cmttypes.Tx{txBz}))
507508

508-
var txGasLimit uint64
509-
if memTx != nil {
510-
if gasTx, ok := memTx.(GasTx); ok {
511-
txGasLimit = gasTx.GetGas()
512-
}
513-
}
514-
515509
// only add the transaction to the proposal if we have enough capacity
516510
if (txSize + ts.totalTxBytes) <= maxTxBytes {
517511
// If there is a max block gas limit, add the tx only if the limit has
518512
// not been met.
519513
if maxBlockGas > 0 {
520-
if (txGasLimit + ts.totalTxGas) <= maxBlockGas {
521-
ts.totalTxGas += txGasLimit
514+
if (gasWanted + ts.totalTxGas) <= maxBlockGas {
515+
ts.totalTxGas += gasWanted
522516
ts.totalTxBytes += txSize
523517
ts.selectedTxs = append(ts.selectedTxs, txBz)
524518
}

baseapp/baseapp.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ func (app *BaseApp) runTx(mode sdk.ExecMode, txBytes []byte, tx sdk.Tx) (gInfo s
873873

874874
switch mode {
875875
case execModeCheck:
876-
err = app.mempool.Insert(ctx, tx)
876+
err = app.mempool.InsertWithGasWanted(ctx, tx, gasWanted)
877877
if err != nil {
878878
return gInfo, nil, anteEvents, err
879879
}
@@ -1067,18 +1067,18 @@ func (app *BaseApp) PrepareProposalVerifyTx(tx sdk.Tx) ([]byte, error) {
10671067
// ProcessProposal state internally will be discarded. <nil, err> will be
10681068
// returned if the transaction cannot be decoded. <Tx, nil> will be returned if
10691069
// the transaction is valid, otherwise <Tx, err> will be returned.
1070-
func (app *BaseApp) ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, error) {
1070+
func (app *BaseApp) ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, uint64, error) {
10711071
tx, err := app.txDecoder(txBz)
10721072
if err != nil {
1073-
return nil, err
1073+
return nil, 0, err
10741074
}
10751075

1076-
_, _, _, err = app.runTx(execModeProcessProposal, txBz, tx)
1076+
gInfo, _, _, err := app.runTx(execModeProcessProposal, txBz, tx)
10771077
if err != nil {
1078-
return nil, err
1078+
return nil, 0, err
10791079
}
10801080

1081-
return tx, nil
1081+
return tx, gInfo.GasWanted, nil
10821082
}
10831083

10841084
func (app *BaseApp) TxDecode(txBytes []byte) (sdk.Tx, error) {

baseapp/testutil/mock/mocks.go

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

types/mempool/mempool.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,30 @@ import (
77
sdk "github.com/cosmos/cosmos-sdk/types"
88
)
99

10+
type Tx struct {
11+
Tx sdk.Tx
12+
GasWanted uint64
13+
}
14+
15+
func NewMempoolTx(tx sdk.Tx, gasWanted uint64) Tx {
16+
return Tx{
17+
Tx: tx,
18+
GasWanted: gasWanted,
19+
}
20+
}
21+
22+
type GasTx interface {
23+
GetGas() uint64
24+
}
25+
1026
type Mempool interface {
1127
// Insert attempts to insert a Tx into the app-side mempool returning
1228
// an error upon failure.
1329
Insert(context.Context, sdk.Tx) error
1430

31+
// Insert with a custom gas wanted value
32+
InsertWithGasWanted(context.Context, sdk.Tx, uint64) error
33+
1534
// Select returns an Iterator over the app-side mempool. If txs are specified,
1635
// then they shall be incorporated into the Iterator. The Iterator is not thread-safe to use.
1736
Select(context.Context, [][]byte) Iterator
@@ -31,7 +50,7 @@ type ExtMempool interface {
3150
Mempool
3251

3352
// SelectBy use callback to iterate over the mempool, it's thread-safe to use.
34-
SelectBy(context.Context, [][]byte, func(sdk.Tx) bool)
53+
SelectBy(context.Context, [][]byte, func(Tx) bool)
3554
}
3655

3756
// Iterator defines an app-side mempool iterator interface that is as minimal as
@@ -43,7 +62,7 @@ type Iterator interface {
4362
Next() Iterator
4463

4564
// Tx returns the transaction at the current position of the iterator.
46-
Tx() sdk.Tx
65+
Tx() Tx
4766
}
4867

4968
var (
@@ -53,7 +72,7 @@ var (
5372

5473
// SelectBy is compatible with old interface to avoid breaking api.
5574
// In v0.52+, this function is removed and SelectBy is merged into Mempool interface.
56-
func SelectBy(ctx context.Context, mempool Mempool, txs [][]byte, callback func(sdk.Tx) bool) {
75+
func SelectBy(ctx context.Context, mempool Mempool, txs [][]byte, callback func(Tx) bool) {
5776
if ext, ok := mempool.(ExtMempool); ok {
5877
ext.SelectBy(ctx, txs, callback)
5978
return

types/mempool/mempool_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func fetchTxs(iterator mempool.Iterator, maxBytes int64) []sdk.Tx {
139139
if numBytes += txSize; numBytes > maxBytes {
140140
break
141141
}
142-
txs = append(txs, iterator.Tx())
142+
txs = append(txs, iterator.Tx().Tx)
143143
i := iterator.Next()
144144
iterator = i
145145
}

types/mempool/noop.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ var _ ExtMempool = (*NoOpMempool)(nil)
1616
// is FIFO-ordered by default.
1717
type NoOpMempool struct{}
1818

19-
func (NoOpMempool) Insert(context.Context, sdk.Tx) error { return nil }
20-
func (NoOpMempool) Select(context.Context, [][]byte) Iterator { return nil }
21-
func (NoOpMempool) SelectBy(context.Context, [][]byte, func(sdk.Tx) bool) {}
22-
func (NoOpMempool) CountTx() int { return 0 }
23-
func (NoOpMempool) Remove(sdk.Tx) error { return nil }
19+
func (NoOpMempool) Insert(context.Context, sdk.Tx) error { return nil }
20+
func (NoOpMempool) InsertWithGasWanted(context.Context, sdk.Tx, uint64) error { return nil }
21+
func (NoOpMempool) Select(context.Context, [][]byte) Iterator { return nil }
22+
func (NoOpMempool) SelectBy(context.Context, [][]byte, func(Tx) bool) {}
23+
func (NoOpMempool) CountTx() int { return 0 }
24+
func (NoOpMempool) Remove(sdk.Tx) error { return nil }

types/mempool/priority_nonce.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,10 @@ func (mp *PriorityNonceMempool[C]) NextSenderTx(sender string) sdk.Tx {
189189
}
190190

191191
cursor := senderIndex.Front()
192-
return cursor.Value.(sdk.Tx)
192+
return cursor.Value.(Tx).Tx
193193
}
194194

195-
// Insert attempts to insert a Tx into the app-side mempool in O(log n) time,
195+
// InsertWithGasWanted attempts to insert a Tx into the app-side mempool in O(log n) time,
196196
// returning an error if unsuccessful. Sender and nonce are derived from the
197197
// transaction's first signature.
198198
//
@@ -201,7 +201,7 @@ func (mp *PriorityNonceMempool[C]) NextSenderTx(sender string) sdk.Tx {
201201
//
202202
// Inserting a duplicate tx with a different priority overwrites the existing tx,
203203
// changing the total order of the mempool.
204-
func (mp *PriorityNonceMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error {
204+
func (mp *PriorityNonceMempool[C]) InsertWithGasWanted(ctx context.Context, tx sdk.Tx, gasWanted uint64) error {
205205
mp.mtx.Lock()
206206
defer mp.mtx.Unlock()
207207
if mp.cfg.MaxTx > 0 && mp.priorityIndex.Len() >= mp.cfg.MaxTx {
@@ -210,6 +210,8 @@ func (mp *PriorityNonceMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error
210210
return nil
211211
}
212212

213+
memTx := NewMempoolTx(tx, gasWanted)
214+
213215
sigs, err := mp.cfg.SignerExtractor.GetSigners(tx)
214216
if err != nil {
215217
return err
@@ -247,12 +249,12 @@ func (mp *PriorityNonceMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error
247249
// changes.
248250
sk := txMeta[C]{nonce: nonce, sender: sender}
249251
if oldScore, txExists := mp.scores[sk]; txExists {
250-
if mp.cfg.TxReplacement != nil && !mp.cfg.TxReplacement(oldScore.priority, priority, senderIndex.Get(key).Value.(sdk.Tx), tx) {
252+
if mp.cfg.TxReplacement != nil && !mp.cfg.TxReplacement(oldScore.priority, priority, senderIndex.Get(key).Value.(Tx).Tx, tx) {
251253
return fmt.Errorf(
252254
"tx doesn't fit the replacement rule, oldPriority: %v, newPriority: %v, oldTx: %v, newTx: %v",
253255
oldScore.priority,
254256
priority,
255-
senderIndex.Get(key).Value.(sdk.Tx),
257+
senderIndex.Get(key).Value.(Tx).Tx,
256258
tx,
257259
)
258260
}
@@ -270,14 +272,23 @@ func (mp *PriorityNonceMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error
270272

271273
// Since senderIndex is scored by nonce, a changed priority will overwrite the
272274
// existing key.
273-
key.senderElement = senderIndex.Set(key, tx)
275+
key.senderElement = senderIndex.Set(key, memTx)
274276

275277
mp.scores[sk] = txMeta[C]{priority: priority}
276278
mp.priorityIndex.Set(key, tx)
277279

278280
return nil
279281
}
280282

283+
func (mp *PriorityNonceMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error {
284+
var gasLimit uint64
285+
if gasTx, ok := tx.(GasTx); ok {
286+
gasLimit = gasTx.GetGas()
287+
}
288+
289+
return mp.InsertWithGasWanted(ctx, tx, gasLimit)
290+
}
291+
281292
func (i *PriorityNonceIterator[C]) iteratePriority() Iterator {
282293
// beginning of priority iteration
283294
if i.priorityNode == nil {
@@ -341,8 +352,8 @@ func (i *PriorityNonceIterator[C]) Next() Iterator {
341352
return i
342353
}
343354

344-
func (i *PriorityNonceIterator[C]) Tx() sdk.Tx {
345-
return i.senderCursors[i.sender].Value.(sdk.Tx)
355+
func (i *PriorityNonceIterator[C]) Tx() Tx {
356+
return i.senderCursors[i.sender].Value.(Tx)
346357
}
347358

348359
// Select returns a set of transactions from the mempool, ordered by priority
@@ -376,7 +387,7 @@ func (mp *PriorityNonceMempool[C]) doSelect(_ context.Context, _ [][]byte) Itera
376387
}
377388

378389
// SelectBy will hold the mutex during the iteration, callback returns if continue.
379-
func (mp *PriorityNonceMempool[C]) SelectBy(ctx context.Context, txs [][]byte, callback func(sdk.Tx) bool) {
390+
func (mp *PriorityNonceMempool[C]) SelectBy(ctx context.Context, txs [][]byte, callback func(Tx) bool) {
380391
mp.mtx.Lock()
381392
defer mp.mtx.Unlock()
382393

0 commit comments

Comments
 (0)