[P1] Open Questions: Block Gas Limits, Fees, Cost per Participant, and System TX Prioritization #928
Priority: Medium
Summary
- Introduces a governance-controlled minimum gas price (
FeeParams.min_gas_price) enforced at consensus level via a customTxFeeChecker, replacing the currentnilfee checker that allows zero-fee transactions - Adds a
NetworkDutyFeeBypassDecoratorthat exempts protocol-obligation messages (PoC, validations, BLS, weight distributions) from fees, following the existingLiquidityPoolFeeBypassDecoratorpattern - Proposes initial gas price of
10ngonka(~$0.00046/tx at current GNK price), making sustained spam attacks cost real money while keeping individual transactions under a cent
Key design decisions
- Consensus-level enforcement via
TxFeeChecker(runs in bothCheckTxandDeliverTx), not per-validatorapp.tomlwhich isCheckTx-only and can be bypassed by block proposers - Recursive
MsgExecunpacking to prevent wrapping fee-required messages inside authz executions - Governance-adjustable parameter — no chain upgrade needed to tune the gas price
💬 Comments (1)
🔄 Auto-synced from Issue #928 every hour.
Implementation-level review from the current codebase
The two-track approach — consensus-level
TxFeeChecker+NetworkDutyFeeBypassDecoratormodeled on the existingLiquidityPoolFeeBypassDecorator— is the right shape. A few concrete gaps from the current code worth resolving before implementation:1. The "network duty" message set needs to be exhaustive, not exemplary
The summary lists "PoC, validations, BLS, weight distributions" — this is the right category but not the complete msg list. Inference defines ~45 msg types (
tx.pb.go). Concrete classification needed before merge:Clearly network duty (must bypass): - PoC:
MsgSubmitPocBatch,MsgSubmitPocValidation,MsgSubmitPocValidationsV2,MsgPoCV2StoreCommit,MsgSubmitSeed- Validation:MsgValidation,MsgInvalidateInference,MsgRevalidateInference- Weight distribution:MsgMLNodeWeightDistribution- BLS:MsgSubmitDealerPart,MsgSubmitVerificationVector,MsgSubmitGroupKeyValidationSignature,MsgSubmitPartialSignature,MsgRespondDealerComplaints(added in v0.2.13 — seeupgrades/v0_2_13/upgrades.go:148) - Hardware diff:MsgSubmitHardwareDiffAmbiguous (proposal must decide explicitly): -
MsgStartInference/MsgFinishInference— emitted by Transfer Agents per inference. At ~83K inferences/day × 2 chain txs ×10ngonka × 200K gas≈ $80/day TA overhead. Bypass them and TA spam is a free attack surface; charge them and TAs need a per-tx gas reserve change. -MsgClaimRewards— 1 per participant per epoch × 43 epochs/day × N participants. Charging is fine economically (~$0.0005 per claim), but the proposal mentions "Cost per Participant" in the title and this is the dominant per-participant cost. -MsgRequestThresholdSignature— who triggers it? If user-driven (request-to-sign), it's user-paid; if protocol (rotation/recovery), it's network duty. -MsgSettleSubnetEscrow— periodic settlement vs user-triggered settle.A flat enumeration in the proposal (or a typed registry interface alongside
LiquidityPoolFeeBypassDecorator) is the only way to make this auditable.2. Recursive
MsgExecunpacking — needs depth limit and authz grant interactionThe summary correctly flags recursive unpacking. Two follow-ons:
authz.MsgExecchains are syntactically unbounded. Without a max recursion depth, a malicious tx withMsgExec(MsgExec(MsgExec(...MsgValidation...)))either DoS the fee checker or silently fails to enforce. SDK convention is depth ≤ 5.authz.MsgExecfrom grantee will deduct fees from granter, but operators have historically funded only the warm (grantee) account. The v0.2.13 backfill inupgrades.go:148sets up newMsgRespondDealerComplaintsgrants but doesn't address this funding shift. If MsgExec'd network-duty messages bypass entirely, this is moot. If a fee-required user message gets MsgExec'd through cold→warm authz, the granter is silently debited — worth being explicit about.3. Mempool priority interaction with existing
LiquidityPoolFeeBypassDecoratorante.go:165setsPriority: 1_000_000for liquidity pool bypassed txs. IfNetworkDutyFeeBypassDecoratordoesn't apply at least the same priority, in a peak-load mempool spam-but-fee-paying user txs will displace network-duty txs, which is the opposite of intent.The proposal should specify priority levels: - Network duty (PoC/validation/BLS): highest (e.g.,
10_000_000) - Liquidity pool: existing1_000_000- Fee-paying user: priority proportional to gas-price (default SDK behavior) - Zero-fee non-bypass: rejected byTxFeeChecker4.
TxFeeCheckerparity betweenCheckTxandDeliverTxproposer manipulationThe summary correctly notes that
app.tomlisCheckTx-only. One subtlety to call out:config.go:61-67currently relies on per-validatorMinGasPrices. ABCI++ allows a block proposer to include any tx in their proposal —DeliverTxthen runs the chain's fee logic. A customTxFeeCheckerthat readsFeeParams.min_gas_pricefrom chain state correctly closes this. Two implementation notes:TxFeeCheckerMUST read from current block context (ctx.BlockHeight()), not from a cached value, so a governance change tomin_gas_pricetakes effect at the next block, not the next epoch.TxFeeCheckermust never return different fee/priority for the same tx betweenCheckTxandDeliverTx(other than the SDK's standardPriorityhandling).5. Bypass + gas metering: block-gas DoS still possible
Today's
LiquidityPoolFeeBypassDecorator(ante.go:163) waives min-gas-prices but keeps metering — gas is still consumed against the block limit.NetworkDutyFeeBypassDecoratorshould do the same, and the proposal should size the block gas limit to absorb worst-case PoC submission volume:SDK default
MaxGasis 10M (or-1for unlimited). At ~500 participants the per-block PoC budget approaches 1.25M gas — still fine, but worth a sentence in the proposal to confirm the budget and to commit governance to revisiting it as participant count grows.A
GasCapper bypassed-tx (analogous to the existingGasCap: 500000on liquidity pool,ante.go:215) is also worth specifying for network-duty messages, so an oversized PoC submission can't single-handedly dominate a block.6. Initial price
10 ngonka— sanity check + governance escape hatchMath reproduces the proposal's $0.00046/tx claim: - 1 GNK = 10⁹ ngonka, current price ≈ $0.23/GNK - Default tx: 200K gas × 10 ngonka = 2M ngonka = 0.002 GNK = $0.00046 ✓
One gap: for spam to cost real money, the attacker has to be paying actual GNK. New participants today get
genesis_guardian_addresses(params.proto) andBlackListAccountsexclusions, but a fresh adversary-funded account can still pay. With 200K gas × 10 ngonka, a determined attacker spending $46 can burn 100K block-gas budget. The proposal should:min_gas_pricemid-attack —x/groupoperational voting, given v0.2.12's split betweenx/govandx/group(perdocs/voting.md).Outline LGTM, but the network-duty msg enumeration (#1) and the authz grant migration (#2) are blockers for safe deployment. Worth landing a follow-on PR with the typed registry of network-duty msg types before the upgrade.