Skip to content

Commit af5c969

Browse files
authored
feat: add additional flags for mempool (#1901)
* fix prioritynoncemempool and add additional flags * add changelog * fix build * add tests * avoid conflicts * move tx replacement test to mempool * isort
1 parent aed642e commit af5c969

File tree

8 files changed

+151
-51
lines changed

8 files changed

+151
-51
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Improvements
66

77
* [#1898](https://github.com/crypto-org-chain/cronos/pull/1898) Chore: cleanup release by reverting #1892, #1893 and #1850.
8+
* [#1901](https://github.com/crypto-org-chain/cronos/pull/1901) Feat: add mempool.feebump and disable-tx-replacement flags.
89

910

1011
*Oct 30, 2025*

app/app.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ const (
175175
FlagUnsafeIgnoreBlockListFailure = "unsafe-ignore-block-list-failure"
176176
FlagUnsafeDummyCheckTx = "unsafe-dummy-check-tx"
177177

178+
FlagMempoolFeeBump = "mempool.feebump"
179+
180+
FlagDisableTxReplacement = "cronos.disable-tx-replacement"
178181
FlagDisableOptimisticExecution = "cronos.disable-optimistic-execution"
179182
)
180183

@@ -379,14 +382,22 @@ func New(
379382
addressCodec := authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix())
380383

381384
var mpool mempool.Mempool
382-
if maxTxs := cast.ToInt(appOpts.Get(server.FlagMempoolMaxTxs)); maxTxs >= 0 {
385+
mempoolMaxTxs := cast.ToInt(appOpts.Get(server.FlagMempoolMaxTxs))
386+
feeBump := cast.ToInt64(appOpts.Get(FlagMempoolFeeBump))
387+
if mempoolMaxTxs >= 0 && feeBump >= 0 {
383388
// NOTE we use custom transaction decoder that supports the sdk.Tx interface instead of sdk.StdTx
384389
// Setup Mempool and Proposal Handlers
385-
logger.Info("NewPriorityMempool is enabled")
390+
logger.Info("NewPriorityMempool is enabled", "feebump", feeBump)
386391
mpool = mempool.NewPriorityMempool(mempool.PriorityNonceMempoolConfig[int64]{
387392
TxPriority: mempool.NewDefaultTxPriority(),
388393
SignerExtractor: evmapp.NewEthSignerExtractionAdapter(mempool.NewDefaultSignerExtractionAdapter()),
389-
MaxTx: maxTxs,
394+
MaxTx: mempoolMaxTxs,
395+
TxReplacement: func(op, np int64, oTx, nTx sdk.Tx) bool {
396+
// we set a rule which the priority of the new Tx must be {feebump}% more than the priority of the old Tx
397+
// otherwise, the Insert will return error
398+
threshold := 100 + feeBump
399+
return np >= op*threshold/100
400+
},
390401
})
391402
} else {
392403
logger.Info("NoOpMempool is enabled")
@@ -423,6 +434,8 @@ func New(
423434

424435
// The default value of optimisticExecution is enabled.
425436
if !optimisticExecutionDisabled {
437+
// enable optimistic execution
438+
logger.Info("Enable optimistic execution")
426439
baseAppOptions = append(baseAppOptions, baseapp.SetOptimisticExecution())
427440
}
428441

@@ -968,9 +981,19 @@ func New(
968981
app.SetPreBlocker(app.PreBlocker)
969982
app.SetBeginBlocker(app.BeginBlocker)
970983
app.SetEndBlocker(app.EndBlocker)
984+
985+
mempoolCacheMaxTxs := mempoolMaxTxs
986+
if cast.ToBool(appOpts.Get(FlagDisableTxReplacement)) {
987+
mempoolCacheMaxTxs = -1
988+
}
989+
if mempoolCacheMaxTxs >= 0 {
990+
logger.Info("Tx replacement is enabled")
991+
} else {
992+
logger.Info("Tx replacement is disabled")
993+
}
971994
if err := app.setAnteHandler(txConfig,
972995
cast.ToUint64(appOpts.Get(srvflags.EVMMaxTxGasWanted)),
973-
cast.ToInt(appOpts.Get(server.FlagMempoolMaxTxs)),
996+
mempoolCacheMaxTxs,
974997
cast.ToStringSlice(appOpts.Get(FlagBlockedAddresses)),
975998
); err != nil {
976999
panic(err)

cmd/cronosd/cmd/root.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
rosettaCmd "github.com/cosmos/rosetta/cmd"
1313
memiavlcfg "github.com/crypto-org-chain/cronos/store/config"
1414
"github.com/crypto-org-chain/cronos/v2/app"
15+
config2 "github.com/crypto-org-chain/cronos/v2/cmd/cronosd/config"
1516
"github.com/crypto-org-chain/cronos/v2/cmd/cronosd/opendb"
1617
"github.com/crypto-org-chain/cronos/v2/x/cronos"
1718
e2eecli "github.com/crypto-org-chain/cronos/v2/x/e2ee/client/cli"
@@ -272,6 +273,7 @@ func initAppConfig() (string, interface{}) {
272273

273274
MemIAVL memiavlcfg.MemIAVLConfig `mapstructure:"memiavl"`
274275
VersionDB VersionDBConfig `mapstructure:"versiondb"`
276+
Cronos config2.CronosConfig `mapstructure:"cronos"`
275277
}
276278

277279
tpl, cfg := servercfg.AppConfig("")
@@ -280,9 +282,10 @@ func initAppConfig() (string, interface{}) {
280282
Config: cfg.(servercfg.Config),
281283
MemIAVL: memiavlcfg.DefaultMemIAVLConfig(),
282284
VersionDB: DefaultVersionDBConfig(),
285+
Cronos: config2.DefaultCronosConfig(),
283286
}
284287

285-
return tpl + memiavlcfg.DefaultConfigTemplate + DefaultVersionDBTemplate, customAppConfig
288+
return tpl + memiavlcfg.DefaultConfigTemplate + DefaultVersionDBTemplate + config2.DefaultCronosConfigTemplate, customAppConfig
286289
}
287290

288291
// newApp creates the application

cmd/cronosd/config/config.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,17 @@ func SetBech32Prefixes(config *sdk.Config) {
88
config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub)
99
config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub)
1010
}
11+
12+
type CronosConfig struct {
13+
// Set to true to disable tx replacement.
14+
DisableTxReplacement bool `mapstructure:"disable-tx-replacement"`
15+
// Set to true to disable optimistic execution.
16+
DisableOptimisticExecution bool `mapstructure:"disable-optimistic-execution"`
17+
}
18+
19+
func DefaultCronosConfig() CronosConfig {
20+
return CronosConfig{
21+
DisableTxReplacement: false,
22+
DisableOptimisticExecution: false,
23+
}
24+
}

cmd/cronosd/config/toml.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package config
2+
3+
// DefaultCronosConfigTemplate defines the configuration template for cronos configuration
4+
const DefaultCronosConfigTemplate = `
5+
###############################################################################
6+
### Cronos Configuration ###
7+
###############################################################################
8+
9+
[cronos]
10+
11+
# Set to true to disable tx replacement.
12+
disable-tx-replacement = {{ .Cronos.DisableTxReplacement }}
13+
14+
# Set to true to disable optimistic execution (not recommended on validator nodes).
15+
disable-optimistic-execution = {{ .Cronos.DisableOptimisticExecution }}
16+
`

integration_tests/configs/default.jsonnet

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
},
2929
mempool: {
3030
'max-txs': 1000,
31+
'feebump': 10,
3132
},
3233
},
3334
validators: [{

integration_tests/test_basic.py

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,11 @@
2626
deploy_contract,
2727
derive_new_account,
2828
fund_acc,
29-
get_account_nonce,
3029
get_expedited_params,
3130
get_receipts_by_block,
3231
get_sync_info,
3332
modify_command_in_supervisor_config,
3433
remove_cancun_prague_params,
35-
replace_transaction,
3634
send_transaction,
3735
send_txs,
3836
sign_transaction,
@@ -1071,50 +1069,6 @@ def test_textual(cronos):
10711069
assert rsp["code"] == 0, rsp["raw_log"]
10721070

10731071

1074-
def test_tx_replacement(cronos):
1075-
w3 = cronos.w3
1076-
gas_price = w3.eth.gas_price
1077-
nonce = get_account_nonce(w3)
1078-
initial_balance = w3.eth.get_balance(ADDRS["community"])
1079-
txhash = replace_transaction(
1080-
w3,
1081-
{
1082-
"to": ADDRS["community"],
1083-
"value": 1,
1084-
"gasPrice": gas_price,
1085-
"nonce": nonce,
1086-
"from": ADDRS["validator"],
1087-
},
1088-
{
1089-
"to": ADDRS["community"],
1090-
"value": 5,
1091-
"gasPrice": gas_price * 2,
1092-
"nonce": nonce,
1093-
"from": ADDRS["validator"],
1094-
},
1095-
KEYS["validator"],
1096-
)["transactionHash"]
1097-
tx1 = w3.eth.get_transaction(txhash)
1098-
assert tx1["transactionIndex"] == 0
1099-
assert w3.eth.get_balance(ADDRS["community"]) == initial_balance + 5
1100-
1101-
# check that already accepted transaction cannot be replaced
1102-
txhash_noreplacemenet = send_transaction(
1103-
w3,
1104-
{"to": ADDRS["community"], "value": 10, "gasPrice": gas_price},
1105-
KEYS["validator"],
1106-
)["transactionHash"]
1107-
tx2 = w3.eth.get_transaction(txhash_noreplacemenet)
1108-
assert tx2["transactionIndex"] == 0
1109-
1110-
with pytest.raises(ValueError) as exc:
1111-
w3.eth.replace_transaction(
1112-
txhash_noreplacemenet,
1113-
{"to": ADDRS["community"], "value": 15, "gasPrice": gas_price},
1114-
)
1115-
assert "has already been mined" in str(exc)
1116-
1117-
11181072
def test_block_tx_properties(cronos):
11191073
"""
11201074
test block tx properties on cronos network

integration_tests/test_mempool.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
CONTRACTS,
1010
KEYS,
1111
deploy_contract,
12+
get_account_nonce,
13+
replace_transaction,
14+
send_transaction,
1215
send_txs,
1316
sign_transaction,
1417
wait_for_new_blocks,
@@ -138,3 +141,88 @@ def send_with_nonce(nonce):
138141
sender
139142
)
140143
assert orig_nonce + 4 + i == local_nonce
144+
145+
146+
@pytest.mark.flaky(max_runs=3)
147+
def test_tx_replacement(cronos_mempool):
148+
w3 = cronos_mempool.w3
149+
base_fee = w3.eth.get_block("latest")["baseFeePerGas"]
150+
priority_fee = w3.eth.max_priority_fee
151+
nonce = get_account_nonce(w3)
152+
# replace with less than 10% bump, should fail
153+
with pytest.raises(ValueError) as exc:
154+
_ = replace_transaction(
155+
w3,
156+
{
157+
"to": ADDRS["community"],
158+
"value": 1,
159+
"maxFeePerGas": base_fee + priority_fee,
160+
"maxPriorityFeePerGas": priority_fee,
161+
"nonce": nonce,
162+
"from": ADDRS["validator"],
163+
},
164+
{
165+
"to": ADDRS["community"],
166+
"value": 2,
167+
"maxFeePerGas": int((base_fee + priority_fee) * 1.05), # +5% bump
168+
"maxPriorityFeePerGas": int(priority_fee * 1.05),
169+
"nonce": nonce,
170+
"from": ADDRS["validator"],
171+
},
172+
KEYS["validator"],
173+
)["transactionHash"]
174+
assert "tx doesn't fit the replacement rule" in str(exc)
175+
176+
wait_for_new_blocks(cronos_mempool.cosmos_cli(), 1)
177+
nonce = get_account_nonce(w3)
178+
initial_balance = w3.eth.get_balance(ADDRS["community"])
179+
# replace with more than 10% bump, should succeed
180+
txhash = replace_transaction(
181+
w3,
182+
{
183+
"to": ADDRS["community"],
184+
"value": 3,
185+
"maxFeePerGas": base_fee + priority_fee,
186+
"maxPriorityFeePerGas": priority_fee,
187+
"nonce": nonce,
188+
"from": ADDRS["validator"],
189+
},
190+
{
191+
"to": ADDRS["community"],
192+
"value": 5,
193+
"maxFeePerGas": int((base_fee + priority_fee) * 1.15), # +15% bump
194+
"maxPriorityFeePerGas": int(priority_fee * 1.15),
195+
"nonce": nonce,
196+
"from": ADDRS["validator"],
197+
},
198+
KEYS["validator"],
199+
)["transactionHash"]
200+
tx1 = w3.eth.get_transaction(txhash)
201+
assert tx1["transactionIndex"] == 0
202+
assert w3.eth.get_balance(ADDRS["community"]) == initial_balance + 5
203+
204+
# check that already accepted transaction cannot be replaced
205+
txhash_noreplacemenet = send_transaction(
206+
w3,
207+
{
208+
"to": ADDRS["community"],
209+
"value": 10,
210+
"maxFeePerGas": base_fee + priority_fee,
211+
"maxPriorityFeePerGas": priority_fee,
212+
},
213+
KEYS["validator"],
214+
)["transactionHash"]
215+
tx2 = w3.eth.get_transaction(txhash_noreplacemenet)
216+
assert tx2["transactionIndex"] == 0
217+
218+
with pytest.raises(ValueError) as exc:
219+
w3.eth.replace_transaction(
220+
txhash_noreplacemenet,
221+
{
222+
"to": ADDRS["community"],
223+
"value": 15,
224+
"maxFeePerGas": int((base_fee + priority_fee) * 1.15), # +15% bump
225+
"maxPriorityFeePerGas": int(priority_fee * 1.15),
226+
},
227+
)
228+
assert "has already been mined" in str(exc)

0 commit comments

Comments
 (0)